Java annotations - java

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).

Related

How to map values from one column to multiple fields?

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.

How to create a single add method for different entities?

How can I create a single add method that will insert different entities in the jdbc? I want to use annotations and reflection for that.
I have created 2 annotations:
(...)
public #interface Column {
String name();
boolean isPrimaryKey() default false;
}
and
(...)
public #interface Table {
String name();
}
Suppose we have 2 entities/models/etc. : Client and Waiter. For both we should make 2 add methods, each with own INSERT.
If we have like 4 entities, we should have 4 add methods for each entity. Instead of having 4 add methods, how can I make just 1 add method? and by using the annotations and the reflection.
Eg:
#Table(name = "Table_Client")
public class Client{
#Column(name = "ID", isPrimaryKey = true)
private long id;
#Column(name = "FULL_NAME")
private String name;
}
#Table(name = "Table_Waiter")
public class Waiter {
#Column(name = "FULL_NAME", isPrimaryKey = true)
private String name;
#Column(name = "AGE")
private int age;
}
case: db.add(Client c1) => add to the database in the table Table_Client the client c1
db.add(Waiter w1) => add to the database in the table Table_Waiter the waiter w1
and so on...
My idea is to take the class of the given object and scan it for the TYPE annotation to get the table's name. Then, take all the field's annotations and make a dynamic INSERT INTO VALUES query, but the problem is that I can't actually do that, because I can't pass the object's arguments.
Another question: if this can be done, update and delete methods can follow the same path?
I cannot refrain from mentioning how many holes you may find in the road ahead. But judging from the comments, that's the path you want to explore.
First of all, regarding your existing code, you need to apply a retention meta-annotation to your annotations, Column and Table. For example:
#Retention(RetentionPolicy.RUNTIME)
public #interface Column {
String name();
boolean isPrimaryKey() default false;
}
This meta-annotation will ensure that you can read your annotations at runtime through reflection.
Then, you need to inspect the class searching for these annotations at both class and field levels.
The Class class will have everything you need. You should know you can get it from any object by calling the getClass method. It contains a couple of important methods for what you are trying to achieve:
getAnnotation(Class c) will return the annotation if it exists, or null otherwise.
getDeclaredFields will return all declared class fields, even private ones.
At a field level, the Field class provides the following methods:
getAnnotation(Class c), same as above, will return the annotation if it exists, or null otherwise.
getType will return the class associated with the field
Now consider the following piece of code:
public static void inspectClass(Class<?> cls) {
Table t = cls.getAnnotation(Table.class);
if (t != null) {
System.out.print(t.name() + " --> ");
for (Field f: cls.getDeclaredFields()) {
Column c = f.getAnnotation(Column.class);
if (c != null) {
System.out.print(c.name()
+ " "
+ f.getType().getSimpleName()
+ (c.isPrimaryKey() ? " PK" : "") + ", ");
}
}
}
}
Applying this to your Client class, for instance, would return something like:
Table_Client --> ID long PK, FULL_NAME String,
Of course, this needs some work, but the idea is there.
EDIT:
To access values of an instance through reflection at runtime, for creating a dynamic INSERT statement, that could be done by calling get method on the Field class. When dealing with private fields though, it's necessary to tweak the privacy mode first:
f.setAccessible(true);
Object value = f.get(myInstance);

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!

can annotation get context object?

Maybe title "can annotation get context object?" is not correct, but I don't know how to give it a right and clear one.
I use Spring AOP + Java Annotation to save log, here is my code:
CategoryAction.java :
#ServiceTracker(methodDesp="save category, category name:"+this.category.getName())
public String save() throws Exception
{
this.categoryService.save(this.category);
this.setJsonDataSimply(null);
return "save";
}
TrackAdvice.java :
public Object trackAround(ProceedingJoinPoint point) throws Throwable
{
String log = "success";
ServiceTracker tracker = null;
Method method = null;
AbstractAction action = null;
try
{
Object result = point.proceed();
action = (AbstractAction) point.getTarget();
MethodSignature signature = (MethodSignature) point.getSignature();
method = signature.getMethod();
tracker = method.getAnnotation(ServiceTracker.class);
return result;
}
catch (Exception e)
{
log = e.getMessage();
throw e;
}
finally
{
if (tracker != null)
{
String userId = (String) ActionContext.getContext().getSession().get(Constant.USERID);
if (userId == null)
{
userId = "unknown";
}
TrackLog t = new TrackLog();
t.setWhen(new Date());
t.setUserId(userId);
t.setResult(log);
t.setMethodName(action.getClass().getCanonicalName() + "." + method.getName());
t.setMethodDesp(tracker.methodDesp());
this.trackService.save(t);
}
}
}
ServiceTracker is my own annotation, in my TrackAdvice class, I get the current executing method, if the method has a ServiceTracker annotation, then save the methodDesp in annotation to database.
Now the question is the methodDesp in annotation is dynamic, I want to get this object and retrieve its category property.
It seems that Java Annotation doesn't support this, maybe it supports but I don't know how.
What you can do is use some sort of expression language in the annotation value and then run some interpreter in your advice code. One example using SPEL could look like this:
#ServiceTracker(methodDesp="save category, category name: #{category.name}")
And in your advice code, you can then extract the expression token, make use of a SpelExpression and pass it the target reference as root object (you may want to check what's available out of the box in the SPEL API for supporting your use-case(s)).
It seems that Java Annotation doesn't support this
You are correct - there is no way to do this in pure java.
The reason is that because annotations are static metadata that is wired into classes and defined at compile-time (this start to exist only at run-time, not compile-time).
In other words there is no straightforward way to make methodDesp of some annotated method of some class dynamic, since it's value has to be resolved statically, at compile-time.
However, technically there is a way to do something like you want. What I talk about is using javassist to either manipulate or create your classes (and annotations applied to them) at runtime. But be warned that this is rather hacky way and I generally would not recommend to go there.

Pitfalls in getting member variable values in Java with reflection

I have an abstract class as follows. I want to get all the values of member variables.
public abstract class PARAMS {
public static final String NAME1 = "VAL1";
public static final String NAME2 = "VAL2";
public static final String NAME3 = "VAL3";
}
The values are retrieved using reflection as follows.
Field[] fields = PARAMS.class.getFields();
for (Field field : fields) {
String name = field.getName() ;
String value = (String) field.get(name);
}
This is the first time I am experimenting with reflection. Is this a correct way to achieve the goal? I would like to know what are the pitfalls in using reflection in this case.
You code iterates over both static and private fields. So you should check that you iterate over static fields only.
for (Field field : PARAMS.class.getFields()) {
if (Modifiered.isStatic(field.getModifiers())) continue;
String name = field.getName() ;
String value = (String) field.get(PARAMS.class);
}
NB: as Jon mentioned, for static field access the instance parameter is ignored. However, I prefer passing in the class instead of null since this is a better documentation of the indent.
It is however even better practice to annotate your fields with an annotation so that you only get those fields that you really want no other static fields added by other programmers (or even the Java language behind the scenes). If you do so, your code would look like
for (Field field : PARAMS.class.getFields()) {
if (!field.isAnnotationsPresent(YourAnnotation.class)) continue;
String name = field.getName() ;
String value = (String) field.get(PARAMS.class);
}
It's not quite correct - the argument to get should ideally be null for the sake of readability: the point of that argument is to give it a target for when you're retrieving instance fields.
So your code can be just:
Field[] fields = PARAMS.class.getFields();
for (Field field : fields) {
String name = field.getName() ;
String value = (String) field.get(null);
}
Now, this should work... but what are you going to do with these values? Is there any reason why you want to do this rather than creating an immutable Map<String, String> which is exposed directly?
Reflection is fine where it's necessary, but you haven't given enough information to determine whether it's actually necessary in this case.
another problem, getFields return all accessible fields (static or not) of this class and all its superclasses. Not a problem for the specific code you posted, since the only superclass is Object which has no public field.
I would at least test if the field is declared in the correct class - getDeclaringClass() - and if it has the correct return type - getType().
Using an Annotation, as Adrian suggested, is best IMHO.

Categories

Resources