SerializationException of Avro Date Object (Date LogicalType) - serialization

I have a publisher that accepts a GenericRecord class.
#Override
public Future<RecordMetadata> publish(GenericRecord genericRecord) {
Future<RecordMetadata> recordMetadataFuture =
getPublisher().send(new ProducerRecord<>(producerConfiguration.getProperties()
.getProperty(ProducerConfiguration.PROPERTY_NAME_TOPIC), "sample.key",genericRecord));
return recordMetadataFuture;
}
private KafkaProducer<String, GenericRecord> getPublisher() {
return new KafkaProducer<>(producerConfiguration.getProperties());
}
And I have the following avro schema:
{
"type" : "record",
"name" : "SampleDate",
"namespace": "com.sample.data.generated.avro",
"doc" : "sample date",
"fields" : [
{
"name" : "sampleDate",
"type" : {
"type" : "int",
"logicalType" : "date"
}
}
]
}
I have built my own serializer:
Date Serializer:
#Component
public class SampleDateSerializer implements Serializer<GenericRecord> {
private AvroGenericSerializer serializer;
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
serializer = new AvroGenericSerializer(SampleDate.SCHEMA$);
}
#Override
public byte[] serialize(String topic, GenericRecord data) {
return serializer.serialize(data);
}
#Override
public void close() {
}
Generic Serializer:
public class AvroGenericSerializer {
private EncoderFactory avroEncoderFactory;
private DecoderFactory avroDecoderFactory;
private GenericDatumWriter<GenericRecord> avroWriter;
private GenericDatumReader<GenericRecord> avroReader;
public AvroGenericSerializer(Schema schema) {
avroEncoderFactory = EncoderFactory.get();
avroDecoderFactory = DecoderFactory.get();
avroWriter = new GenericDatumWriter<>(schema);
avroReader = new GenericDatumReader<>(schema);
}
public byte[] serialize(GenericRecord data) {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
final BinaryEncoder binaryEncoder = avroEncoderFactory.binaryEncoder(stream, null);
try {
avroWriter.write(data, binaryEncoder);
binaryEncoder.flush();
stream.close();
return stream.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Can't serialize Avro object", e);
}
}
public GenericRecord deserialize(byte[] bytes) {
try {
return avroReader.read(null, avroDecoderFactory.binaryDecoder(bytes, null));
} catch (IOException e) {
throw new RuntimeException("Can't deserialize Avro object", e);
}
}
}
However, when testing my publisher class, I am encountering the following error:
org.apache.kafka.common.errors.SerializationException: Can't convert value of class com.sample.data.generated.avro.SampleDate to class com.sample.message.serialize.SampleDateSerializer specified in value.serializer
Debugging the code, I have found out that the
GenericDatumWriter.write()...
method is returning null when calling the
Conversion conversion = this.getData().getConversionByClass(datum.getClass(), logicalType);
which is called from
org.apache.avro.generic.GenericData
public <T> Conversion<T> getConversionByClass(Class<T> datumClass, LogicalType logicalType) {
Map conversions = (Map)this.conversionsByClass.get(datumClass);
return conversions != null?(Conversion)conversions.get(logicalType.getName()):null;
}
In this regard, is there a way for me to populate the
GenericData.conversionsByClass
Map, so that it can return the correct converter to use for the given
date logicalType?

I have solved it by passing the GenericData object in my GenericDatumWriter.
My Generic Serializer now looks like this:
public AvroGenericSerializer(Schema schema) {
avroEncoderFactory = EncoderFactory.get();
avroDecoderFactory = DecoderFactory.get();
final GenericData genericData = new GenericData();
genericData.addLogicalTypeConversion(new TimeConversions.DateConversion());
avroWriter = new GenericDatumWriter<>(schema, genericData);
avroReader = new GenericDatumReader<>(schema);
}

Related

Spring R2dbc: Is there are way to get constant stream from postgresql database and process them?

I want to fetch records for newly created records in a table in postgresql as a live/continuous stream. Is it possible to use using spring r2dbc? If so what options do I have?
Thanks
You need to use pg_notify and start to listing on it. Any change that you want to see should be wrapped in simple trigger that will send message to pg_notify.
I have an example of this on my github, but long story short:
prepare function and trigger:
CREATE OR REPLACE FUNCTION notify_member_saved()
RETURNS TRIGGER
AS $$
BEGIN
PERFORM pg_notify('MEMBER_SAVED', row_to_json(NEW)::text);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER member_saved_trigger
AFTER INSERT OR UPDATE
ON members
FOR EACH ROW
EXECUTE PROCEDURE notify_member_saved();
In java code prepare listener
#Service
#RequiredArgsConstructor
#Slf4j
class NotificationService {
private final ConnectionFactory connectionFactory;
private final Set<NotificationTopic> watchedTopics = Collections.synchronizedSet(new HashSet<>());
#Qualifier("postgres-event-mapper")
private final ObjectMapper objectMapper;
private PostgresqlConnection connection;
#PreDestroy
private void preDestroy() {
this.getConnection().close().subscribe();
}
private PostgresqlConnection getConnection() {
if(connection == null) {
synchronized(NotificationService.class) {
if(connection == null) {
try {
connection = Mono.from(connectionFactory.create())
.cast(Wrapped.class)
.map(Wrapped::unwrap)
.cast(PostgresqlConnection.class)
.toFuture().get();
} catch(InterruptedException e) {
throw new RuntimeException(e);
} catch(ExecutionException e) {
throw new RuntimeException(e);
}
}
}
}
return this.connection;
}
public <T> Flux<T> listen(final NotificationTopic topic, final Class<T> clazz) {
if(!watchedTopics.contains(topic)) {
executeListenStatement(topic);
}
return getConnection().getNotifications()
.log("notifications")
.filter(notification -> topic.name().equals(notification.getName()) && notification.getParameter() != null)
.handle((notification, sink) -> {
final String json = notification.getParameter();
if(!StringUtils.isBlank(json)) {
try {
sink.next(objectMapper.readValue(json, clazz));
} catch(JsonProcessingException e) {
log.error(String.format("Problem deserializing an instance of [%s] " +
"with the following json: %s ", clazz.getSimpleName(), json), e);
Mono.error(new DeserializationException(topic, e));
}
}
});
}
private void executeListenStatement(final NotificationTopic topic) {
getConnection().createStatement(String.format("LISTEN \"%s\"", topic)).execute()
.doOnComplete(() -> watchedTopics.add(topic))
.subscribe();
}
public void unlisten(final NotificationTopic topic) {
if(watchedTopics.contains(topic)) {
executeUnlistenStatement(topic);
}
}
private void executeUnlistenStatement(final NotificationTopic topic) {
getConnection().createStatement(String.format("UNLISTEN \"%s\"", topic)).execute()
.doOnComplete(() -> watchedTopics.remove(topic))
.subscribe();
}
}
start listiong from controller
#GetMapping("/events")
public Flux<ServerSentEvent<Object>> listenToEvents() {
return Flux.merge(listenToDeletedItems(), listenToSavedItems())
.map(o -> ServerSentEvent.builder()
.retry(Duration.ofSeconds(4L))
.event(o.getClass().getName())
.data(o).build()
);
}
#GetMapping("/unevents")
public Mono<ResponseEntity<Void>> unlistenToEvents() {
unlistenToDeletedItems();
unlistenToSavedItems();
return Mono.just(
ResponseEntity
.status(HttpStatus.I_AM_A_TEAPOT)
.body(null)
);
}
private Flux<Member> listenToSavedItems() {
return this.notificationService.listen(MEMBER_SAVED, Member.class);
}
private void unlistenToSavedItems() {
this.notificationService.unlisten(MEMBER_SAVED);
}
but remember that if something broke then you lost pg_notify events for some time so it is for non-mission-citical solutions.

Hibernate QueryException Named parameter not bound error

I keep getting the error org.hibernate.QueryException: Named parameter not bound : item when I start my transaction in JPA using EntityManager createNativeQuery. I have my code below utilizing the entity manager, along with my embeddedID class (for the composite key) and my persistence entity bean. Is there a problem in my query syntax? I am not sure as I've tried multiple ways of formatting the sql (coming from a properties file where there resides multiple sqls used throughout the project, and attempting to persist data to an oracle db). I am not sure why I keep falling on this error. I want to persist this data to my oracle database but this error keeps preventing that.
Query from query.properties file:
insertPromoData =INSERT INTO TEST.U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY, U_TIMESTAMP) VALUES (:item, :location, :wkStart, :numberOfWeeks, :type, :fcstId, :quantity, SYSDATE)
Embeddedable Class for establishing composite primary key on the target table:
#Embeddable
public class PromoID implements Serializable {
#Column(name = "ITEM")
private String item;
#Column(name = "LOC")
private String loc;
#Column(name = "WK_START")
private Date weekStart;
#Column(name = "TYPE")
private int type;
#Column(name = "FCSTID")
private String forecastId;
#Column(name = "U_TIMESTAMP")
private Timestamp insertTS;
public PromoID() {
}
public PromoID (String item, String loc, Date weekStart, int type, String forecastId, Timestamp insertTS) {
this.item = item;
this.loc = loc;
this.weekStart = weekStart;
this.type = type;
this.forecastId = forecastId;
this.insertTS = insertTS;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Date getWeekStart() {
return weekStart;
}
public void setWeekStart(Date weekStart) {
this.weekStart = weekStart;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getForecastId() {
return forecastId;
}
public void setForecastId(String forecastId) {
this.forecastId = forecastId;
}
public Timestamp getInsertTS() {
return insertTS;
}
public void setInsertTS(Timestamp insertTS) {
this.insertTS = insertTS;
}
//removed hashcode and equals methods for simplicity
Persistence Entity Bean:
#Entity
#Table(name = "U_USER_PROMO")
public class InsertPromoData {
#EmbeddedId
private PromoID id;
/*#Column(name="BATCH_ID")
String batchID;*/
#Column(name="ITEM")
String item;
#Column(name="LOC")
String loc;
#Column(name="WK_START")
String weekStart;
#Column(name="TYPE")
String type;
#Column(name="FCSTID")
String forecastId;
#Column(name="U_TIMESTAMP")
String insertTS;
#Column(name="NUMBER_OF_WEEKS")
String numberOfWeeks;
#Column(name="QTY")
String qty;
#Id
#AttributeOverrides(
{
#AttributeOverride(name = "item",column = #Column(name="ITEM")),
#AttributeOverride(name = "loc", column = #Column(name="LOC")),
#AttributeOverride(name = "weekStart", column = #Column(name="WK_START")),
#AttributeOverride(name = "type", column = #Column(name="TYPE")),
#AttributeOverride(name = "forecastId", column = #Column(name="FCSTID"))
}
)
public PromoID getId() {
return id;
}
public void setId(PromoID id) {
this.id = id;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getWeekStart() {
return weekStart;
}
public void setWeekStart(String weekStart) {
this.weekStart = weekStart;
}
public String getNumberOfWeeks() {
return numberOfWeeks;
}
public void setNumberOfWeeks(String numberOfWeeks) {
this.numberOfWeeks = numberOfWeeks;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getForecastId() {
return forecastId;
}
public void setForecastId(String forecastId) {
this.forecastId = forecastId;
}
public String getQty() {
return qty;
}
public void setQty(String qty) {
this.qty = qty;
}
public String getInsertTS() {
return insertTS;
}
public void setInsertTS(String insertTS) {
this.insertTS = insertTS;
}
}
My dao OracleImpl.java using EntityManager for persisting:
public void insertPromoData(List<InsertPromoData> insertData) {
logger.debug("Execution of method insertPromoData in Dao started");
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
System.out.println("Beginning transaction for insertPromoData");
Query query = em.createNativeQuery(env.getProperty("insertPromoData"));
for (InsertPromoData promoData : insertData) {
query.setParameter("item", promoData.getItem());
query.setParameter("location", promoData.getLoc());
query.setParameter("wkStart", promoData.getWeekStart());
query.setParameter("numberOfWeeks", promoData.getNumberOfWeeks());
query.setParameter("type", promoData.getType());
query.setParameter("fcstId", promoData.getForecastId());
query.setParameter("quantity", Double.valueOf(promoData.getQty()));
}
query.executeUpdate();
System.out.println("Data for promo persisted");
em.getTransaction().commit();
}
catch(Exception e) {
logger.error("Exception in beginning transaction");
e.printStackTrace();
}
finally {
em.clear();
em.close();
}
logger.debug("Execution of method insertPromoData in Dao ended");
}
PromoValidator.java class:
List <InsertPromoData> insertPromos = new ArrayList<>();
promo.forEach(record -> {
if (record.getErrorList().size() == 0) {
rowsSuccessful++;
record.setItem(record.getItem());
record.setLoc(record.getLoc());
record.setNumber_Of_Weeks(record.getNumber_Of_Weeks());
record.setForecast_ID(record.getForecast_ID());
record.setType(record.getType());
record.setUnits(record.getUnits());
record.setWeek_Start_Date(record.getWeek_Start_Date());
insertPromos = (List<InsertPromoData>) new InsertPromoData();
for (InsertPromoData insertPromoData : insertPromos) {
insertPromoData.setItem(record.getItem());
insertPromoData.setLoc(record.getLoc());
insertPromoData.setWeekStart(LocalDate.parse(record.getWeek_Start_Date()));
insertPromoData.setNumberOfWeeks(Integer.parseInt(record.getNumber_Of_Weeks()));
insertPromoData.setType(Integer.parseInt(record.getType()));
insertPromoData.setForecastId(record.getForecast_ID());
insertPromoData.setQty(Double.parseDouble(record.getUnits()));
}
} else {
if (rowsFailure == 0) {
Util.writeHeaderToFile(templateCd, errorFile);
}
rowsFailure++;
Util.writeErrorToFile(templateCd, errorFile, record, record.getErrorList());
}
});
errorFile.close();
successFile.close();
OracleImpl.insertPromoData(insertPromos);
One of the reason this can happen is when insertData List you are passing is empty.
If I use below code ( please note that I have removed few columns to simplify it on my test environment with H2 database) - I get the error you described if empty list is passed and that's because there is indeed nothing to bind for the name parameter as the loop is not executed.
try {
em.getTransaction().begin();
System.out.println("Beginning transaction for insertPromoData");
Query query = em.createNativeQuery(
"INSERT INTO U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY) VALUES (:item, :location, :wkStart, :numberOfWeeks, :type, :fcstId, :quantity)");
for (InsertPromoData promoData : insertData) {
query.setParameter("item", promoData.getId().getItem());
query.setParameter("location", promoData.getId().getLoc());
query.setParameter("wkStart", promoData.getId().getWeekStart());
query.setParameter("numberOfWeeks", promoData.getNumberOfWeeks());
query.setParameter("type", promoData.getId().getType());
query.setParameter("fcstId", promoData.getId().getForecastId());
query.setParameter("quantity", Double.valueOf(promoData.getQty()));
}
query.executeUpdate();
System.out.println("Data for promo persisted");
em.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
em.clear();
em.close();
}
The error I get is
org.hibernate.QueryException: Named parameter not bound : item
at org.hibernate.query.internal.QueryParameterBindingsImpl.verifyParametersBound(QueryParameterBindingsImpl.java:210)
at org.hibernate.query.internal.AbstractProducedQuery.beforeQuery(AbstractProducedQuery.java:1425)
at org.hibernate.query.internal.NativeQueryImpl.beforeQuery(NativeQueryImpl.java:249)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1610)
at com.danielme.blog.nativesql.dao.UserDao.insertPromoData(UserDao.java:99)
However - if I pass non-empty list - this works as expected
SQLCustomQuery:72 - Starting processing of sql query [INSERT INTO U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY) VALUES (:item, :location, :wkStart, :numberOfWeeks, :type, :fcstId, :quantity)]
AbstractFlushingEventListener:74 - Flushing session
I also encountered same problem. I noticed that I was defining the parameter in the query but I was not setting its value in:
query.setParameter(parameterToBeSet,parameterValue)
For example,
String hql = "FROM COLLEGE FETCH ALL PROPERTIES WHERE studentId = :studentId AND departmentId = :departmentId AND studentName = :studentName";
DBConnectionManager.getInstance().invokeCallableOnSessionMasterDB(session -> {
Query<CollegeEntity> query = session.createQuery(hql);
query.setParameter("studentId", studentId);
query.setParameter("departmentId", departmentId);
values.addAll(query.list());
});
As you can see the student name value was not set. So, after adding:
query.setParameter("studentName", studentName);
It got solved.
GETRequest : localhost:8080/getbyCity/chennai,mumbai,kolkata -
error ---> when I send this request I got------->"Hibernate QueryException Named parameter not bound error".
Answer: when we send list of parameter we should mention the "/" at the End. ((( localhost:8080/getbyCity/chennai,mumbai,kolkata/ ))). Set the bound using "/".
In my case I have put same parameter name to the procedure as:
#Param("managementAccess") Boolean managementAccess,
#Param("managementAccess") String dealerCode,
#Param("managementAccess") String dealerName,
Here the parameter name is different but inside string the name is same.
Solution is:
#Param("managementAccess") Boolean managementAccess,
#Param("dealerCode") String dealerCode,
#Param("dealerName") String dealerName,

#JsonIdentityReference does not recognize equal values

I'm trying to serialize an object (Root), with some duplicated entries of MyObject. Just want store the whole objects one, I'm using #JsonIdentityReference, which works pretty well.
However, I realize that it will generate un-deserializable object, if there're equal objects with different reference. I wonder if there's a configuration in Jackson to change this behavior, thanks!
#Value
#AllArgsConstructor
#NoArgsConstructor(force = true)
class Root {
private List<MyObject> allObjects;
private Map<String, MyObject> objectMap;
}
#Value
#AllArgsConstructor
#NoArgsConstructor(force = true)
#JsonIdentityReference
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class MyObject {
private String id;
private int value;
}
public class Main {
public static void main() throws JsonProcessingException {
// Constructing equal objects
val obj1 = new MyObject("a", 1);
val obj2 = new MyObject("a", 1);
assert obj1.equals(obj2);
val root = new Root(
Lists.newArrayList(obj1),
ImmutableMap.of(
"lorem", obj2
)
);
val objectMapper = new ObjectMapper();
val json = objectMapper.writeValueAsString(root);
// {"allObjects":[{"id":"a","value":1}],"objectMap":{"lorem":{"id":"a","value":1}}}
// Note here both obj1 and obj2 are expanded.
// Exception: Already had POJO for id
val deserialized = objectMapper.readValue(json, Root.class);
assert root.equals(deserialized);
}
}
I'm using Jackson 2.10.
Full stacktrace:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.lang.String) [[ObjectId: key=a, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=java.lang.Object]] (through reference chain: Root["objectMap"]->java.util.LinkedHashMap["lorem"]->MyObject["id"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1714)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1257)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:157)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:527)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:364)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
at Main.main(Main.java:53)
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [[ObjectId: key=a, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=java.lang.Object]]
at com.fasterxml.jackson.annotation.SimpleObjectIdResolver.bindItem(SimpleObjectIdResolver.java:24)
at com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.bindItem(ReadableObjectId.java:57)
at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:101)
at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:83)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369)
... 14 more
As I mentioned earlier, this setup only works if obj1 == obj2, as the two objects with same ID should be identity-equal. In that case, the second object would also net get expanded during serialization (alwaysAsId = false only expands the first object).
However, if you want to have this setup and are fine with the serialization, you could use a custom Resolver for deserialization that stores a single instance per key:
#JsonIdentityReference(alwaysAsId = false)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", resolver = CustomScopeResolver.class)
static class MyObject {
private String id;
// ...
}
class CustomScopeResolver implements ObjectIdResolver {
Map<String, MyObject> data = new HashMap<>();
#Override
public void bindItem(final IdKey id, final Object pojo) {
data.put(id.key.toString(), (MyObject) pojo);
}
#Override
public Object resolveId(final IdKey id) {
return data.get(id.key);
}
#Override
public ObjectIdResolver newForDeserialization(final Object context) {
return new CustomScopeResolver();
}
#Override
public boolean canUseFor(final ObjectIdResolver resolverType) {
return false;
}
}
NEW EDIT: Apparently, its very easy: Just turn on objectMapper.configure(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID, true); so that the DefaultSerializerProvider uses a regular Hashmap instead of an IdentityHashMap to manage the serialized beans.
DEPRECATED: Update for Serialization: It is possible to achieve this by adding a custom SerializationProvider:
class CustomEqualObjectsSerializerProvider extends DefaultSerializerProvider {
private final Collection<MyObject> data = new HashSet<>();
private final SerializerProvider src;
private final SerializationConfig config;
private final SerializerFactory f;
public CustomEqualObjectsSerializerProvider(
final SerializerProvider src,
final SerializationConfig config,
final SerializerFactory f) {
super(src, config, f);
this.src = src;
this.config = config;
this.f = f;
}
#Override
public DefaultSerializerProvider createInstance(final SerializationConfig config, final SerializerFactory jsf) {
return new CustomEqualObjectsSerializerProvider(src, this.config, f);
}
#Override
public WritableObjectId findObjectId(final Object forPojo, final ObjectIdGenerator<?> generatorType) {
// check if there is an equivalent pojo, use it if exists
final Optional<MyObject> equivalentObject = data.stream()
.filter(forPojo::equals)
.findFirst();
if (equivalentObject.isPresent()) {
return super.findObjectId(equivalentObject.get(), generatorType);
} else {
if (forPojo instanceof MyObject) {
data.add((MyObject) forPojo);
}
return super.findObjectId(forPojo, generatorType);
}
}
}
#Test
public void main() throws IOException {
// Constructing equal objects
final MyObject obj1 = new MyObject();
obj1.setId("a");
final MyObject obj2 = new MyObject();
obj2.setId("a");
assert obj1.equals(obj2);
final Root root = new Root();
root.setAllObjects(Collections.singletonList(obj1));
root.setObjectMap(Collections.singletonMap(
"lorem", obj2));
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializerProvider(
new CustomEqualObjectsSerializerProvider(
objectMapper.getSerializerProvider(),
objectMapper.getSerializationConfig(),
objectMapper.getSerializerFactory()));
final String json = objectMapper.writeValueAsString(root);
System.out.println(json); // second object is not expanded!
}

Why wrap GenericRowWithSchema using InternalRow in Dataset.collectFromPlan

Look at Dataset.collectFromPlan
private def collectFromPlan(plan: SparkPlan): Array[T] = {
val objProj = GenerateSafeProjection.generate(deserializer :: Nil)
plan.executeCollect().map { row =>
objProj(row).get(0, null).asInstanceOf[T]
}
}
the generated code for objProj is:
class SpecificSafeProjection extends org.apache.spark.sql.catalyst.expressions.codegen.BaseProjection {
private Object[] references;
private InternalRow mutableRow;
private Object[] values;
private org.apache.spark.sql.types.StructType schema;
public SpecificSafeProjection(Object[] references) {
this.references = references;
mutableRow = (InternalRow) references[references.length - 1];
schema = (org.apache.spark.sql.types.StructType) references[0];
}
public void initialize(int partitionIndex) {
}
public java.lang.Object apply(java.lang.Object _i) {
........
final org.apache.spark.sql.Row value = new org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema(values, schema);
if (false) {
mutableRow.setNullAt(0);
} else {
mutableRow.update(0, value);
}
return mutableRow;
}
}
And my question is:
The generated method SpecificSafeProjection.apply returns mutableRow: InternalRow who wraps value:GenericRowWithSchema, why not return value:GenericRowWithSchema directly??

JTable for JPanel with multiple type JComponent (Swing)

This is a long question, but all code is needed (I was thinking a basic question, with not common components).
I need to build a JTable with multiple Columns with multiple type Custom JPanel (with JSlider + JComboBox + JTextField) in only one Column.
I was reading: http://pekalicious.com/blog/custom-jpanel-cell-with-jbuttons-in-jtable/
In this question, I will use one Column with only one Customized JPanel with name PanelSpinnerRadioButton.
The Custom Panel:PanelSpinnerRadioButton.java
public class PanelSpinnerRadioButton extends JPanel {
private final JRadioButton jrbOption01= new JRadioButton("01");
private final JRadioButton jrbOption02 = new JRadioButton("12");
private final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));
private final JPanel jpPanelSpinnerRadioButton = new JPanel();
PanelSpinnerRadioButton() {
this(new PanelSpinnerRadioButtonData(false, 20, 40));
}
PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
super();
jpPanelSpinnerRadioButton.setLayout(new BoxLayout(jpPanelSpinnerRadioButton, BoxLayout.LINE_AXIS));
jpPanelSpinnerRadioButton.add(jrbOption01);
jpPanelSpinnerRadioButton.add(jrbOption02);
jpPanelSpinnerRadioButton.add(Box.createRigidArea(new Dimension(5,0)));
jpPanelSpinnerRadioButton.add(new JSeparator(JSeparator.VERTICAL));
jpPanelSpinnerRadioButton.add(Box.createRigidArea(new Dimension(5,0)));
jpPanelSpinnerRadioButton.add(jspnValues);
//Change States of RadioButtons (will be readOnly, ButtonGroup is not needed)
jrbOption02.setSelected(data.getOption());
jrbOption01.setSelected(!data.getOption());
//Change States of Spinner
((SpinnerNumberModel)jspnValues.getModel()).setValue(data.getFrom());
((SpinnerNumberModel)jspnValues.getModel()).setMaximum(data.getSize());
init();
}
private void init() {
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
setBackground(new Color(0, 0, 0, 0/*64*/));
add(jpPanelSpinnerRadioButton);
}
// Used in PSRBTableCellEditor.getCellEditorValue()
public PanelSpinnerRadioButtonData getData() {
return new PanelSpinnerRadioButtonData(
jrbOption02.isSelected(),
(Integer)((SpinnerNumberModel)jspnValues.getModel()).getValue(),
(Integer)((SpinnerNumberModel)jspnValues.getModel()).getMaximum()
);
}
}
Here the Image Result of before Custom JPanel
Data Type for Custom Panel:PanelSpinnerRadioButtonData.java
public class PanelSpinnerRadioButtonData {
private boolean opt02 = false;
private Integer from = 0;
private Integer size = 1;
PanelSpinnerRadioButtonData() {
this(false, 5, 10);
}
PanelSpinnerRadioButtonData(boolean opt02, Integer from, Integer size) {
this.opt02 = opt02;
this.from = from;
this.size = size;
}
public boolean getOption() { return opt02; }
public Integer getFrom() { return from; }
public Integer getSize() { return size; }
}
Now the coded to handle my Custom JPanel (with inner JComponents)
the Table Model:PSRBTableModel.java
public class PSRBTableModel extends AbstractTableModel {
private final Object[][] data;
private final Object[] columns;
public PSRBTableModel(Object[][] data, Object[] columns) {
// super();
this.data = data;
this.columns = columns;
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (data != null) {
if (data.length > 0) {
if (data[rowIndex][columnIndex] instanceof PanelSpinnerRadioButton) {
return (PanelSpinnerRadioButton)data[rowIndex][columnIndex];
}
return data[rowIndex][columnIndex];
}
}
return null;
}
public int getColumnCount() {
return ((columns == null) ? 0: columns.length);
}
public int getRowCount() {
return ((data == null) ? 0: data.length);
}
public Class getColumnClass(int columnIndex) {
if (data != null) {
if (data.length > 0) {
if (data[0][columnIndex] instanceof PanelSpinnerRadioButton) {
return PanelSpinnerRadioButton.class;
}
return data[0][columnIndex].getClass();
}
}
return null;
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (data != null) {
if (data.length > 0) {
if (data[0][columnIndex] instanceof PanelSpinnerRadioButton) {
//PanelSpinnerRadioButton Is not Editable
return false;
}
}
}
return true;
}
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
public String getColumnName(int columnIndex) {
return (String)columns[columnIndex];
}
}
Now the Renderer:PSRBTableCellRenderer.java
public class PSRBTableCellRenderer implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Object output = null;
if (value instanceof PanelSpinnerRadioButtonData) {
output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
}
return (Component)output;
}
}
And the EditorCell:PSRBTableCellEditor.java
public class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
protected Object output;
PSRBTableCellEditor() {
}
public Object getCellEditorValue() {
//Returns the value contained in the editor.
if (output instanceof PanelSpinnerRadioButton) {
return ((PanelSpinnerRadioButton)output).getData();
}
return null;
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
//Sets an initial value for the editor.
if (value instanceof PanelSpinnerRadioButtonData) {
output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
return (PanelSpinnerRadioButton)output;
}
return null;
}
}
Test the Before Code (I'm working with JFrame)
String[] hdrsObjects = new String[]{"PanelSpinnerRadioButton Class Column"};
Object[][] objectMatrix = new Object[3][hdrsObjects.length];
objectMatrix[0][0] = new PanelSpinnerRadioButton();
objectMatrix[1][0] = new PanelSpinnerRadioButton();
objectMatrix[2][0] = new PanelSpinnerRadioButton();
JTable jtbl = new JTable(new PSRBTableModel(objectMatrix, hdrsObjects));
//jtbl.setDefaultRenderer(PanelSpinnerRadioButton.class, new PSRBTableCellRenderer());
//jtbl.setDefaultEditor(PanelSpinnerRadioButton.class, new PSRBTableCellEditor());
jtbl.getColumn("PanelSpinnerRadioButton Class Column").setCellRenderer(new PSRBTableCellRenderer());
jtbl.getColumn("PanelSpinnerRadioButton Class Column").setCellEditor(new PSRBTableCellEditor());
add(new JScrollPane(jtbl));
But I can't see the JTable with (3 rows with Custom JPanel 'PanelSpinnerRadioButton')
I have this exception...
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableUI.paintCell(SynthTableUI.java:684)
at javax.swing.plaf.synth.SynthTableUI.paintCells(SynthTableUI.java:580)
at javax.swing.plaf.synth.SynthTableUI.paint(SynthTableUI.java:364)
at javax.swing.plaf.synth.SynthTableUI.update(SynthTableUI.java:275)
at javax.swing.JComponent.paintComponent(JComponent.java:780)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JViewport.paint(JViewport.java:728)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:306)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent.paint(JComponent.java:1042)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
at java.awt.Container.paint(Container.java:1975)
at java.awt.Window.paint(Window.java:3912)
at javax.swing.RepaintManager$4.run(RepaintManager.java:842)
at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
QUESTION
What's wrong?
Why I can't see the JTable with 3 PanelSpinnerRadioButton?
EDITION
Please consider this scenario (Another Custom JPanel handled by the same TableCellRenderer PSRBTableCellRenderer and TableCellEditor PSRBTableCellEditor)
class PanelButton extends JPanel {
private final JPanel jpPanelButton = new JPanel();
// the ActionListener does not matter now
JButton jbtAction = new JButton("Action");
PanelButton() {
this(new PanelButtonEmpty());
}
PanelButton(PanelButtonEmpty data) {
super();
jpPanelButton.setLayout(new BoxLayout(jpPanelButton, BoxLayout.LINE_AXIS));
jpPanelButton.add(jbtAction);
/*Overridable method call in constructor*/
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
setBackground(new Color(0, 0, 0, 0/*64*/));
add(jpPanelButton);
}
public PanelButtonEmpty getData() {
return new PanelButtonEmpty();
}
public void setData(PanelButtonEmpty data) {
}
}
I was thinking in a totally empty class (to cast the value in TableCellRenderer PSRBTableCellEditor)
class PanelButtonEmpty {
PanelButtonEmpty() { }
//public boolean getLazy() { return true; }
//public void setLazy(boolean b) {}
}
In this case I have two types of Custom JPanel for this reason I have
for TableCellEditor
public class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private Object output;
#Override public Object getCellEditorValue() {
if (output instanceof PanelSpinnerRadioButton) {
return ((PanelSpinnerRadioButton)output).getData();
}
if (output instanceof PanelButton) {
return ((PanelButton)output).getData();
}
return null;
}
#Override public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
if (value instanceof PanelSpinnerRadioButtonData) {
output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
return (PanelSpinnerRadioButton)output;
}
if (value instanceof PanelButtonEmpty) {
output = new PanelButton((PanelButtonEmpty)value);
return (PanelButton)output;
}
return null;
}
}
for TableCellRenderer
public class PSRBTableCellRenderer implements TableCellRenderer {
private Object output = null;
#Override public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof PanelSpinnerRadioButtonData) {
output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
}
if (value instanceof PanelButtonEmpty) {
output = new PanelButton((PanelButtonEmpty)value);
}
return (Component)output;
}
}
QUESTIONS
How to avoid your Second Warning (in the new scenario)?
It is a waste to create each time a component in the TableCellRenderer#getTableCellRendererComponent(...).
How to handle with Objects different to here described?
PD:
I'm not sure to put this second part question in another post with the same source code...
objectMatrix[0][0] = new PanelSpinnerRadioButton();
Should be set PanelSpinnerRadioButtonData to the TableModel, rather than a component such as PanelSpinnerRadioButton.
output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
It is a waste to create each time a component in the TableCellRenderer#getTableCellRendererComponent(...).
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableTest {
public JComponent makeUI() {
String[] hdrsObjects = {"PanelSpinnerRadioButton Class Column"};
Object[][] objectMatrix = new Object[3][hdrsObjects.length];
objectMatrix[0][0] = new PanelSpinnerRadioButtonData(false, 10, 40);
objectMatrix[1][0] = new PanelSpinnerRadioButtonData(true, 20, 40);
objectMatrix[2][0] = new PanelSpinnerRadioButtonData(false, 30, 40);
JTable table = new JTable(new DefaultTableModel(objectMatrix, hdrsObjects));
table.setRowHeight(30);
TableColumn tc = table.getColumn("PanelSpinnerRadioButton Class Column");
tc.setCellRenderer(new PSRBTableCellRenderer());
tc.setCellEditor(new PSRBTableCellEditor());
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
try {
for (UIManager.LookAndFeelInfo laf: UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(laf.getName())) {
UIManager.setLookAndFeel(laf.getClassName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TableTest().makeUI());
f.setSize(240, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class PanelSpinnerRadioButtonData {
private boolean opt02 = false;
private Integer from = 0;
private Integer size = 1;
PanelSpinnerRadioButtonData() {
this(false, 5, 10);
}
PanelSpinnerRadioButtonData(boolean opt02, Integer from, Integer size) {
this.opt02 = opt02;
this.from = from;
this.size = size;
}
public boolean getOption() {
return opt02;
}
public Integer getFrom() {
return from;
}
public Integer getSize() {
return size;
}
}
class PanelSpinnerRadioButton extends JPanel {
public final JRadioButton jrbOption01 = new JRadioButton("01");
public final JRadioButton jrbOption02 = new JRadioButton("12");
public final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));
private final JPanel panel = new JPanel();
PanelSpinnerRadioButton() {
this(new PanelSpinnerRadioButtonData(false, 20, 40));
}
PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
super();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.add(jrbOption01);
panel.add(jrbOption02);
panel.add(Box.createRigidArea(new Dimension(5, 0)));
panel.add(new JSeparator(JSeparator.VERTICAL));
panel.add(Box.createRigidArea(new Dimension(5, 0)));
panel.add(jspnValues);
ButtonGroup bg = new ButtonGroup();
bg.add(jrbOption01);
bg.add(jrbOption02);
((SpinnerNumberModel) jspnValues.getModel()).setMaximum(data.getSize());
setData(data);
init();
}
private void init() {
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
setBackground(new Color(0, 0, 0, 0));
add(panel);
}
public void setData(PanelSpinnerRadioButtonData data) {
if (data.getOption()) {
jrbOption02.setSelected(true);
} else {
jrbOption01.setSelected(true);
}
((SpinnerNumberModel) jspnValues.getModel()).setValue(data.getFrom());
}
// Used in PSRBTableCellEditor.getCellEditorValue()
public PanelSpinnerRadioButtonData getData() {
return new PanelSpinnerRadioButtonData(
jrbOption02.isSelected(),
(Integer) ((SpinnerNumberModel) jspnValues.getModel()).getValue(),
(Integer) ((SpinnerNumberModel) jspnValues.getModel()).getMaximum());
}
}
class PSRBTableCellRenderer implements TableCellRenderer {
private final PanelSpinnerRadioButton renderer = new PanelSpinnerRadioButton();
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof PanelSpinnerRadioButtonData) {
renderer.setData((PanelSpinnerRadioButtonData) value);
}
return renderer;
}
}
class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private final PanelSpinnerRadioButton editor = new PanelSpinnerRadioButton();
#Override public Object getCellEditorValue() {
return editor.getData();
}
#Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
if (value instanceof PanelSpinnerRadioButtonData) {
editor.setData((PanelSpinnerRadioButtonData) value);
}
return editor;
}
}