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);
Related
Is there a way to avoid calling this.field for every field in a class ?
public class Test {
private String name;
private String email;
public Test(String name, String email) {
// I want to avoid this
this.name = name;
this.email = email;
}
public Test(Test test) {
// Something like this would be perfect, setting both name and email to test
this(test);
}
}
The use of this is only required in cases of name collisions, to resolve the ambiguity.
Some programmers like me prefer using the this. prefix routinely, whereas other use only where necessary.
See Answer by Wasserman for an example of how to avoid naming collision.
Use the IDE, Luke
Your IDE will generate constructors, accessors (getters/setters), equals & hashCode, toString, and so on. So you need not type this.; let the machine do the typing.
Use custom settings to control whether you want the IDE to include or omit this. prefixes.
record
You may be interested in using the records feature, new in Java 16+. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably.
With a record, by default, the compiler implicitly writes the constructor, getters, equals & hashCode, and toString. The implicitly created constructor populates each and every member field on your behalf. You write none of that code.
Here is your entire example class when written as a record. No this required. All your member fields are automatically assigned.
public record Test ( String name , String email ) {}
Be cautious in using records. The reason for their invention was not writing less code. The reason was to provide an explicit mechanism for transmitting immutable data transparently, a “nominal tuple” in academic-speak. Less boilerplate coding is merely a nice side-effect. I highly recommend reading JEP 395 for more explanation.
Tip: You can combine the two points of this Answer. Ask your IDE to generate a full-blown class by starting with a record.
Write a record with all your member fields listed in the parentheses.
Invoke your IDE to convert from a record to a class.
Voilà, you have a complete class with constructor, accessors, equals & hashCode, and toString all written out with an absolute minimum of typing by you.
For example, in IntelliJ 2022, choosing Convert record to class from the light-bulb icon menu turns this:
public record Test ( String name , String email ) {}
… into this:
package work.basil.example.recs;
import java.util.Objects;
public final class Test
{
private final String name;
private final String email;
public Test ( String name , String email )
{
this.name = name;
this.email = email;
}
public String name ( ) { return name; }
public String email ( ) { return email; }
#Override
public boolean equals ( Object obj )
{
if ( obj == this ) { return true; }
if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
var that = ( Test ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.email , that.email );
}
#Override
public int hashCode ( )
{
return Objects.hash( name , email );
}
#Override
public String toString ( )
{
return "Test[" +
"name=" + name + ", " +
"email=" + email + ']';
}
}
Caveat: That result may not be the default. I may have altered the settings in IntelliJ.
Sorry, the only way to avoid this is to have different names for your constructor parameters and for your class fields.
public Test(String _name, String _email) {
// I want to avoid this
name = _name;
email = _email;
}
That said, you might have better luck using Java 16+'s record syntax.
As suggested, using records is the easiest way:
public record Test (String name, String email) {
}
That's all you need. What you then get:
A constructor that takes all arguments, in the same order as the field list
A method for each field. This does not start with get. In this case, the methods are name() and email().
equals, hashCode and toString implementations that use all fields.
There is no need for a copy constructor, because every field is automatically final.
If you want, you can add extra constructors. However, they must delegate to the automatically generated constructor, because that's the one that sets the fields. Adding additional utility methods is also fine.
And if needed, you can add validation to the generated constructor. There's special syntax that allows you to omit all the field names:
public record Test (String name, String email) {
public Test {
Objects.requireNonNull(name);
Objects.requireNonNull(email);
}
}
The assignments are done for you, there's no need to type those either.
You need this.x everytime, if there are 2 or more variables, which are called x and you want to call the attribute variable x.
The this keyword is used, to point on an attribute variable of the created instance (object) of the class.
There could be an attribute, that is called x, and a local variable which is called x too.
I need to compare two objects of the same class excluding some fields.
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:
private String z;
private Date createdAt;
private Date updatedAt;
}
How can i find if the two objects of the above class are equal excluding createdAt and updatedAt values? Since there are a lot of fields in this class, i don't want to compare each of them one by one.
Please don't give AssertJ's recursive comparison solution as I don't need it for UnitTests.
Thank you in Advance!
If overriding Object::equals and Object::hashCode is not an option, we can use the Comparator API to construct a corresponding comparator:
final Comparator<Class1> comp = Comparator.comparing(Class1::getA)
.thenComparing(Class1::getB)
.thenComparing(Class1::getC)
.
.
.
.thenComparing(Class1::getZ);
Unfortunately, there is no way to do this without comparing all fields that should be equal.
The quickest way without writing any code is Lombok
Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. If you need to read more on what it can and does, go here.
The way to implement what you need is pretty straightforward:
// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Getter // Generate getters for each field
#Setter // Generate setters for each field
public class Class1
{
#EqualsAndHashCode.Include // Include this field
private Long identity;
private String testStr1; // This field is not annotated with Include so it will not be included in the functions.
// ... any other fields
}
Lombok can do a lot more than this. For more information on #EqualsAndHashCode refer to this.
You can always use #EqualsAndHashCode.Exclude for a quicker solution to your use case:
#EqualsAndHashCode
#Getter // Generate getters for each field
#Setter // Generate setters for each field
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:
private String z;
#EqualsAndHashCode.Exclude
private Date createdAt;
#EqualsAndHashCode.Exclude
private Date updatedAt;
}
Try overriding equals method like below :
import java.util.Date;
import java.util.Objects;
public final class Class1 {
private String a;
private String b;
private String c;
private String z;
private Date createdAt;
private Date updatedAt;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Class1 class1 = (Class1) o;
return Objects.equals(a, class1.a) && Objects.equals(b, class1.b) && Objects.equals(c, class1.c) && Objects.equals(z, class1.z);
}
#Override
public int hashCode() {
return Objects.hash(a, b, c, z);
}
}
I addition to the Comparator and hashCode()/equals method, you could also use Reflections.
Create an annotation to exclude certain fields:
Blacklisting Example:
#Retention(RetentionPolicy.RUNTIME) //
#Target(ElementType.FIELD) //on class level
public #interface IngoreForEqualCheck { /* tagging only */ }
Use Reflection to analyze the objects you want to compare, by using pClass.getFields() and/or pClass.getDeclaredFields() on the objects' class. This may be even different classes.
Iterate over all fields that are NOT tagged to be ignored, compare values.
Optimizations
To extend on the blacklisting from above, also introduce whitelisting: also create an annotation UseForEqualCheck to only check those fields.
For improved speed, when analyzing the respective class and its fields, you can create iterable lists of the fields to check, and instead of doing the Reflection fields analysis each time, simply use the lists.
Normally you would use equals() on the detected field values. You could also a) tag the class with another custom annotation, or b) check the fields for any whitelisting/blacklisting annotations, so that you will reliably use your new method for embedded/inherited/delegated annotated classes.
Warning
As with all reflections, you might get into trouble when analyzing hierarchies of classes, that have been modified during the compile process (javac) by annotation preprocessors, or by bytecode weaving. This mostly refers to Java EE aka Jakarta, but can happen anywhere where behind-the-scenes functionality is incorporated in your code, or runtime behavior is changed, like with injections, aspect oriented libraries etc.
#Renis1235 's Lombok solution is the easiest.
However if for some reason in different contexts the equals can mean different things and you don't want to change the default Equals and Hashcode behaviour, I would advice to assign default values to the fields you want to exclude, and then use equals.
(Assuming you can change the objects)
For example:
Class1 a = ...;
Class1 b = ...;
a.createdAt = null;
b.createdAt = null;
a.updatedAt = null;
b.updatedAt = null;
boolean isEqualExcludingTimestamps = a.equals(b);
Use Apache Commons-Lang
CompareToBuilder.reflectionCompare(expected, argument, "someField");
I have following classes:
public class Note extends RealmObject {
#PrimaryKey
private String id;
private Template template;
// other primitive fields, getters & setters
}
public class Template extends RealmObject {
private String name;
private String color;
// other primitive fields, getters & setters
}
I get my data from backend via Retrofit & Gson, so I have ready-to-use java objects in response.
Let's imagine that backend returns me same three Notes each time I call it.
When I get the list of Note objects, I do the following:
private void fetchNotesAndSave() {
List<Notes> notes = getNotesViaRetrofit();
Realm realm = Realm.getInstance(mContext);
realm.beginTransaction();
realm.copyToRealmOrUpdate(notes);
realm.commitTransaction();
realm.close();
}
After that I call these lines to check count of stored objects:
int notesCount = mRealm.where(Note.class).findAll().size();
int templatesCount = mRealm.where(Template.class).findAll().size();
For the first time:
notesCount == 3;
templatesCount == 3;
That's right. But, if I call the server again, get same notes (same primaryKey ids), and call fetchNotesAndSave() again, I'll get these results:
notesCount == 3;
templatesCount == 6;
Each time I call copyToRealmOrUpdate(), nested objects, that are inside of objects with primaryKey are duplicated - not updated.
Is there any way to change this behaviour?
Please let me know if you need more information. Thanks in advance!
It is because your Template class doesn't have any primary key. In that case these objects are inserted again as there is no guarantee that the referenced template objects safely can be updated, even if they are part of another object that has a primary key.
If you add a #PrimaryKey to your template class it should work as you expect it to.
If you can't provide a PK as suggested, you might want to use the following work around to avoid duplicates.
for (Note note: notes) {
realm.where(Note.class)
.equalTo("id", note.getId())
.findFirst()
.getTemplate()
.deleteFromRealm();
}
realm.copyToRealmOrUpdate(notes);
This is a follow up question to Retrieve audited entities name, old value and new value of the given revision
I have figured out how to get the two revision of an entity but don't see any easy to find the difference between the two. Is there anything in envers that will help doing a diff of an entity at different revisions? Or any good libraries?
I would be really cool if I could get the property modified (_mod) field fields.
So what I came up with to make life easier was to create an annotation to mark the fields I was interested in comparing. Without I ended up having to get with sticking to naming conversation like only only using methods that start with 'get'. I found there was a lot of corner cases with this approach.
The annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AuditCompare {
public String name() default "";
public CompareType compareBy() default CompareType.string;
enum CompareType {
string, count
}
}
which gets used like
#Entity
#Audited
public class Guideline {
.....
#AuditCompare
private String name;
#AuditCompare
private String owner;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, mappedBy="guideline")
private Set<GuidelineCheckListItem> checkListItems = new HashSet<GuidelineCheckListItem>();
.........
}
Since envers audits both the Set changing and the object of the set as two different events I didn't want to compare if the set change. Then to do the comparison I have method that looks like
private void findMatchingValues(Object oldInstance, Object newInstance, ActivityEntry entry) {
try {
Class oldClass = oldInstance.getClass();
for (Field someField : oldClass.getDeclaredFields()) {
if (someField.isAnnotationPresent(AuditCompare.class)) {
String name = someField.getAnnotation(AuditCompare.class).name();
name = name.equals("") ? someField.getName() : name;
Method method = oldClass.getDeclaredMethod(getGetterName(name));
if(someField.getAnnotation(AuditCompare.class).compareBy().equals(AuditCompare.CompareType.count)) {
int oldSize = getCollectionCount(oldInstance, method);
int newSize = getCollectionCount(newInstance, method);
if (oldSize != newSize) entry.addChangeEntry(name, oldSize, newSize);
} else {
Object oldValue = getObjectValue(oldInstance, method);
Object newValue = getObjectValue(newInstance, method);
if (!oldValue.equals(newValue)) entry.addChangeEntry(name, oldValue, newValue);
}
}
}
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
There's no support for that in Envers currently. You will have to compare the objects yourself.
The Envers API is more for viewing the history. It won't tell you what changed but you can fetch two revisions and looks for differences. The fact is, it doesn't store what changed, it stores all the fields of that entity at that point in time. It would have to compare each one to figure out the differences, so you will need to write that code.
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).