How to map values from one column to multiple fields? - java

Say I use Open CSV and have something like:
public class MyDto {
#CsvBindByName(column = "AFBP")
String placeholderA;
#CsvBindByNames({
#CsvBindByName(column = "ABCD"),
#CsvBindByName(column = "AFEL")
})
String placeholderB;
#CsvBindByNames({
#CsvBindByName(column = "ABCD"),
#CsvBindByName(column = "ALTM")
})
String placeholderC;
#Override
public String toString() {
return "placeholder A = " + placeholderA + ", placeholderB = " + placeholderB + ", placeholderC = " + placeholderC;
}
}
After deserializing
var csv = "AFBP,ABCD\nthis is A,this is B and C";
I got
placeholder A = this is A, placeholderB = null, placeholderC = this is B and C
rather than what I need:
placeholder A = this is A, placeholderB = this is B and C, placeholderC = this is B and C
Question
Is there a way, using this library or similar (providing support for annotations), to deserialize the CSV that a one-column-to-many-fields mapping is possible?

No, this is not possible, at least with OpenCSV version 5.7.1. Yet, this might change with future releases.
The reason originates from the way how OpenCSV registers a Beans/Pojos field to column mappings via the HeaderColumnNameMappingStrategy. This is used by default for CsvToBeanBuilder:
This builder is intelligent enough to guess the mapping strategy according to the following strategy:
If a mapping strategy is explicitly set, it is always used.
If CsvBindByPosition or CsvCustomBindByPosition is present, ColumnPositionMappingStrategy is used.
Otherwise, HeaderColumnNameMappingStrategy is used. This includes the case when CsvBindByName or CsvCustomBindByName are being used. The
annotations will automatically be recognized.
Internally, the class HeaderColumnNameMappingStrategy will call registerBinding(..). In this context, the columnName of a Pojo (here: MyPojo) is used as a key for the mapping info in fieldMap.
Sadly, the current implementation does not check, in line 168, if a mapping is already present for a key (eg, ABCD). Thus, it will overwrite the first bound field placeholderB with a new binding for placeholderC. As a result, the parsing will only recognize that it should map values from the csv input to the field placeholderC, as you observed.
Side node: I tested your code and can confirm the output as given in your question.
The only idea that comes to my mind:
Write your own implementation of a headers (to) column name strategy.
Start by extending from HeaderNameBaseMappingStrategy. This way you could handle existing field-to-column mappings for multiple occurrences correctly.
Obviously, you need to register your custom, or multi-field strategy with the CsvToBeanBuilder#withMappingStrategy(..) method.
More consequently, you could open a change request to support the desired behavior. This way, the existing mapping strategy could be improved to cope with such a requirement brought up by your question here.
Currently, no other options seem to exist.

Related

Object differ hasChanges where no changes should be detected

I'm using java-object-diff to get differences between two objects parsed from xml by JAXB. In below example, I'm using the same string to test if I get no differences, however log.info("has changes: " + diff5.hasChanges()); logs true.
JAXBContext context1 = JAXBContext.newInstance(Item.class);
Unmarshaller m1 = context1.createUnmarshaller();
Item base = (Item) m1.unmarshal(new StringReader(s));
Item working = (Item) m1.unmarshal(new StringReader(s));
DiffNode diff5 = ObjectDifferBuilder
.buildDefault()
.compare(working, base);
log.info("has changes: " + diff5.hasChanges());
diff5.visit((node, visit) -> {
final Object baseValue = node.canonicalGet(base);
final Object workingValue = node.canonicalGet(working);
final String message = node.getPath() + " changed from " +
baseValue + " to " + workingValue;
System.out.println(message);
});
The message I get from System.out.println is always the same, saying it has changed from null to <the actual value> This happens for every property. E.g.
content changed from null to Mit dem Wasserinonisator
I have verified that the both Items have the same content and none of the both actualy is not null, but the exact same content.
Item is a pojo with many subclasses (all getters and setters are present), e.g.
public class Item {
#XmlElement(name = "ASIN", required = true)
protected String asin;
#XmlElement(name = "ParentASIN")
protected String parentASIN;
#XmlElement(name = "Errors")
protected Errors errors;
#XmlElement(name = "DetailPageURL")
protected String detailPageURL;
#XmlElement(name = "ItemLinks")
protected ItemLinks itemLinks;
#XmlElement(name = "SalesRank")
protected String salesRank;
#XmlElement(name = "SmallImage")
protected Image smallImage;
}
Is there any way to make java-object-diff work, to make it compare the values correctly?
After taking a closer look at your code I know what's wrong. The first problem is the fact, that JAXB doesn't generate equals methods. For the most part, that's not a problem, because the ObjectDiffer can establish the relationship between objects based on the hierarchy. Things get more complicated when ordered or unordered Collections are involved, because the ObjectDiffer needs some kind of way to establish the relationship between the collection items in the base and working instance. By default it relies on the lookup mechanism of the underlying collection (which typically involves on or more of the methods hashCode, equals or compareTo.)
In your case this relationship cannot be established, because none of your classes (but especially those contained in Lists and Sets) implement a proper equals method. This means that instances are only ever equal to themselves. This is further complicated by the fact, that the responsible classes represent value objects and don't have any hard identifier, that could be used to easily establish the relationship. Therefore the only option is to provide custom equals methods that simply compare all properties. The consequence is, that the slightest change on those objects will cause the ObjectDiffer to mark the base version as REMOVED and the working version as ADDED. But it will also not mark them as CHANGED, when they haven't actually changed. So that's something.
I'm not sure how easy it is to make JAXB generate custom equals methods, so here are some alternative solutions possible with java-object-diff:
Implement your own de.danielbechler.diff.identity.IdentityStrategy for the problematic types and provide them to the ObjectDifferBuilder, like so (example uses Java 8 Lambdas):
ObjectDifferBuilder
.startBuilding()
.identity()
.ofCollectionItems(ItemLinks.class, "itemLink").via((working, base) -> {
ItemLink workingItemLink = (ItemLink) working;
ItemLink baseItemLink = (ItemLink) base;
return StringUtils.equals(workingItemLink.getDescription(), baseItemLink.getDescription())
&& StringUtils.equals(workingItemLink.getURL(), baseItemLink.getURL());
})
// ...
.and().build();
Ignore problematic properties during comparison. Obviously this may not be what you want, but it's an easy solution in case you don't really care about the specific object.
ObjectDifferBuilder
.startBuilding()
.inclusion()
.exclude().type(Item.ImageSets.class)
.and().build();
A solution that causes JAXB to generate custom equals methods would be my preferred way to go. I found another post that claims it's possible, so maybe you want to give this a try first, so you don't have to customize your ObjectDiffer.
I hope this helps!

Validating field length by making use of Jooq and JPA's #Column annotation

I am trying to validate the data input by the user by making use of JSR 303 validations. One validation that I am trying to implement is to check that the size of the inputted value for each field does not exceed the maximum size of the corresponding column.
In order to map a field to a database column I am making use of JPA's #Column annotation as follows:
#ComplexValidation
public class Person {
#Column(table = "PERSON_DETAILS", name = "FIRST_NAME")
private String firstName;
}
The #ComplexValidation annotation on the Person class, is a JSR 303 custom constraint validator that I am trying to implement, which basically tries to carry out the following steps:
Retreives all fields in the class annotated with #Column annotation
It extracts the table name from the annotation and uses it to load the corresponding JOOQ generated class representing the table
It extracts the field name from the annotation and uses it to load the data type and size for the corresponding column
Is there any way in Jooq where I can retrieve the Jooq Generated class based on the table name? My first attempt can be found below, however it does not work since table(tableName) returns an SQLTable not a TableImpl object:
Column columnAnnotation = field.getDeclaredAnnotation(Column.class);
if (columnAnnotation != null) {
String tableName = columnAnnotation.table();
String fieldName = columnAnnotation.name();
TableField tableField = (TableField) ((TableImpl) table(tableName)).field(fieldName);
int columnLength = tableField.getDataType().length();
if (fieldValue.length() > columnLength) {
constraintValidatorContext
.buildConstraintViolationWithTemplate("exceeded maximum length")
.addPropertyNode(field.getName())
.addConstraintViolation();
}
}
Any other suggestions are welcome :)
Assuming you only have one generated schema (e.g. PUBLIC), you can access tables from there:
Table<?> table = PUBLIC.getTable(tableName);
See Schema.getTable(String)

Handling null values in protobuffers

I am working on something which fetches data from database and constructs protobuff message. Given the possibility that null values can be fetched from the database for certain fields , I will get Null-pointer exception while trying to construct the protobuff message. Getting to know that null is not supported in protobuffs from the thread http://code.google.com/p/protobuf/issues/detail?id=57, I am wondering whether the only other way to handle NPE getting thrown is to insert manual checks into the java file corresponding to the proto like below!
message ProtoPerson{
optional string firstName = 1;
optional string lastName = 2;
optional string address1 = 3;
}
ProtoPerson.Builder builder = ProtoPerson.Builder.newBuilder();
if (p.getFirstName() != null) builder.setFirstName(p.getFirstName());
if (p.getLastName() != null) builder.setLastName(p.getLastName());
if (p.getAddress1() != null) builder.setAddress1(p.getAddress1());
...
So can someone please clarify whether there is any other possible efficient way to handle the null values during protobuff construction??
Disclaimer: Answer from a Googler using protobufs on a daily basis. I'm by no means representing Google in any way.
Name your proto Person instead of PersonProto or ProtoPerson. Compiled protobufs are just class definitions specified by the language you are using, with some improvements. Adding "Proto" is extra verbosity.
Use YourMessage.hasYourField() instead of YourMessage.getYourField() != null. Default value for protobuf string is an empty string, which does NOT equal to null. Whereas, no matter whether your field is unset or cleared or empty string, .hasYourField() always returns false. See default values for common protobuf field types.
You've probably known, but I wanna say explicitly: Don't programmatically set a protobuf field to null. Even for outside of protobuf, null causes all sorts of problems. Use .clearYourField() instead.
Person.Builder class does NOT have a .newBuilder() method. Person class does. Understand the Builder Pattern like this: You create a new builder only if you do not have it yet.
A rewrite of your protobuf:
message Person {
optional string first_name = 1;
optional string last_name = 2;
optional string address_1 = 3;
}
A rewrite of your logic:
Person thatPerson = Person.newBuilder()
.setFirstName("Aaa")
.setLastName("Bbb")
.setAddress1("Ccc")
.build();
Person.Builder thisPersonBuilder = Person.newBuilder()
if (thatPerson.hasFirstName()) {
thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}
if (thatPerson.hasLastName()) {
thisPersonBuilder.setLastName(thatPerson.getLastName());
}
if (thatPerson.hasAddress1()) {
thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}
Person thisPerson = thisPersonBuilder.build();
And if thatPerson is a person object that you created that has attribute values that could be an empty string, empty spaces or null, then I'd recommend using Guava's Strings library:
import static com.google.common.base.Strings.nullToEmpty;
Person.Builder thisPersonBuilder = Person.newBuilder()
if (!nullToEmpty(thatPerson.getFirstName()).trim().isEmpty()) {
thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}
if (!nullToEmpty(thatPerson.hasLastName()).trim().isEmpty()) {
thisPersonBuilder.setLastName(thatPerson.getLastName());
}
if (!nullToEmpty(thatPerson.hasAddress1()).trim().isEmpty()) {
thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}
Person thisPerson = thisPersonBuilder.build();
Proto 3
wrappers.proto supports nullable values:
string(StringValue),
int(Int32Value),
bool(BoolValue)
and etc
Example
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message ProtoPerson {
google.protobuf.StringValue firstName = 1;
google.protobuf.StringValue lastName = 2;
google.protobuf.StringValue address1 = 3;
google.protobuf.Int32Value age = 4;
}
There's no easy solution to this. I'd recommend just dealing with the null checks. But if you really want to get rid of them, here are a couple ideas:
You could write a code generator plugin which adds setOrClearFoo() methods to each Java class. The Java code generator provides insertion points for this (see the end of that page).
You could use Java reflection to iterate over the get*() methods of p, call each one, check for null, and then call the set*() method of builder if non-null. This will have the added advantage that you won't have to update your copy code every time you add a new field, but it will be much slower than writing code that copies each field explicitly.

Naive use of java enums' ordinal in the context of JPA spelling disaster in the domain model

I would like to describe a nasty issue that occurs when making naive use of Java enums in the context of JPA entities. Let's take a look at how this problem can occur.
First the domain model:
Say I have an Text JPA entity that represents piece of text (novel, news article, etc.). Here is the JPA entity:
#Entity
public class Text {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String content;
#Enumerated
#ElementCollection
private Set<Style> styles;
//Setters and getters omitted.
To an instance of Text, one or many styles can be applied such as italic, bold and so on. The style is represented as a java enum.
To start with, we assume that the application starts its life with the following enum:
public enum Style {
BOLD, ITALIC
}
The test below will then insert the following lines in a relational database:
Integration test:
#Test
#Rollback(value=false)
public void inEarlyLifePersist() {
Text text =new Text();
text.setContent("This is my beautiful novel...");
text.setStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
text.persist();
}
Data in text table:
# id, content, version
11, This is my beautiful novel..., 0
*Data in text_style table:*
# text, styles
11, 0
11, 1
Then, later on, some ill-advised developer decides to add a new style: STRIKE_THROUGH to our Style enum placing this new enum constant/value as the first one:
public enum Style {
STRIKE_THROUGH, BOLD, ITALIC
}
and then a new record is inserted in DB as follows:
#Test
#Rollback(value=false)
public void afterChangeToEnumPersist() {
Text text =new Text();
text.setContent("This is my beautiful short story...");
text.setStyles(EnumSet.of(Style.STRIKE_THROUGH, Style.BOLD));
text.persist();
}
In text table:
# id, content, version
14, This is my beautiful short story..., 0
And *in text_style table:*
# text, styles
14, 0
14, 1
Obviously, the domain model is seriously compromised now!
My question is what are the possible strategies to avoid spelling disaster in the domain as is the case above (other than the obvious solution to place the STRIKE_THROUGH enum constant after ITALIC)?
edit 1: Obviously I do not want to store strings (see EnumType.STRING) in my database for obvious performance reasons i.e. data retrieval and storage performance would be seriously affected!
You need to redefine your enum like below.
public enum Style {
STRIKE_THROUGH(2), BOLD(0), ITALIC(1)
Style(int code){
this.code=code;
}
}
And implement a Hibernate User type to persist the code.
There's an option (EnumType.STRING) to use the actual name of the enum value (the String returned by { name() } instead of the ordinal. That way you can reorganize your enum values, but then you are tied to the names of the enum values.
The ideal solution would be to be able to declaratively tell the JPA implementation to use an arbitrary property of the enum as the database identifier. But AFAIK, that it's not provided in the current JPA specs, it would be great to have such a feature in future JPA specs.
The answer by Sajan shows how to implement that using a Hibernate-specific feature.
The Enumerated annotation also knows a property that specifies the EnumType. Two types exist: EnumType.ORDINAL and EnumType.STRING. ORDINAL is the default one.
So, if you do it the following way
#Enumerated(EnumType.STRING)
you will see the enumeration names in the DB column (and not the ordinals). Of course, you are now vulnerable to name changes in your enumeration. You have to die one death, but I think, the names are better.
I can't see why people find the enum names more reliable than their ordinals. Actually, there are many good reasons for renaming enums (fixing typos, changed names due to politics or political correctness, etc.), but I can't see any good reason for reordering them.
Both renaming and reordering happens and the only thing which can help is a test. Unfortunately, the best test I can think of will fail on any change. Fortunately, the test can tell what happened and then be fixed easily.
public void testE1IsStable() {
assertEnumUnchanged(E1.class, 4, "bec419c8380dbe9ec3b86a7023a55107");
}
public void testE2IsStable() {
assertEnumUnchanged(E2.class, 3, "1e89e93c6cbdbb7311b814c19d682548");
}
private void assertEnumUnchanged(Class<? extends Enum<?>> enumClass, int expectedCount, String expectedHash) {
final Object[] enumConstants = enumClass.getEnumConstants();
if (expectedCount < enumConstants.length) {
final Object[] shortened = Arrays.copyOf(enumConstants, expectedCount);
assertEquals("Enum constants may be only appended! Ask balteo!",
expectedHash, hashAsString(shortened));
fail("An enum constant has been added! This test needs to be updated. Ask balteo!");
} else if (expectedCount > enumConstants.length) {
fail("Enum constants must not be removed! Ask balteo!");
} else {
assertEquals("Enum constants must not be reordered! If they get renamed, this test must be updated. Ask balteo!",
expectedHash, hashAsString(enumConstants));
}
}
private String hashAsString(Object[] enumConstants) {
final Hasher hasher = Hashing.md5().newHasher();
for (final Object o : enumConstants) hasher.putUnencodedChars(o.toString());
return hasher.hash().toString();
}

Java annotations

I've created simple annotation in Java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Column {
String columnName();
}
and class
public class Table {
#Column(columnName = "id")
private int colId;
#Column(columnName = "name")
private String colName;
private int noAnnotationHere;
public Table(int colId, String colName, int noAnnotationHere) {
this.colId = colId;
this.colName = colName;
this.noAnnotationHere = noAnnotationHere;
}
}
I need to iterate over all fields, that are annotated with Column and get name and value of field and annotation. But I've got problem with getting value of each field, since all of them are of different data type.
Is there anything that would return collection of fields that have certain annotation?
I managed to do it with this code, but I don't think that reflection is good way to solve it.
Table table = new Table(1, "test", 2);
for (Field field : table.getClass().getDeclaredFields()) {
Column col;
// check if field has annotation
if ((col = field.getAnnotation(Column.class)) != null) {
String log = "colname: " + col.columnName() + "\n";
log += "field name: " + field.getName() + "\n\n";
// here i don't know how to get value of field, since all get methods
// are type specific
System.out.println(log);
}
}
Do I have to wrap every field in object, which would implement method like getValue(), or is there some better way around this? Basicly all I need is string representation of each field that is annotated.
edit: yep field.get(table) works, but only for public fields, is there any way how to do this even for private fields? Or do I have to make getter and somehow invoke it?
Every object should has toString() defined. (And you can override this for each class to get a more meaningful representation).
So you where your "// here I don't know" comment is, you could have:
Object value = field.get(table);
// gets the value of this field for the instance 'table'
log += "value: " + value + "\n";
// implicitly uses toString for you
// or will put 'null' if the object is null
Reflection is exactly the way to solve it. Finding out things about types and their members at execution time is pretty much the definition of reflection! The way you've done it looks fine to me.
To find the value of the field, use field.get(table)
Reflection is exactly the way to look at annotations. They are a form of "metadata" attached to the class or method, and Java annotations were designed to be examined that way.
Reflection is one way to process the object (probably the only way if the fields are private and don't have any kind of accessor method). You'll need to look at Field.setAccessible and perhaps Field.getType.
Another approach is to generate another class for enumerating the annotated fields using a compile-time annotation processor. This requires a com.sun API in Java 5, but support is better in the Java 6 JDK (IDEs like Eclipse may require special project configuration).

Categories

Resources