How to avoid overwriting of non-null values with null values? - java

I'm using spring MVC for receiving a JSON from client and automatically create an object from it. The problem is that the client doesn't send to server all the fields that are in the entity, but some fields are null and overwrite existing values calling userDao.persist(user). For example, i have this entity:
#Entity
public class User {
#Id #GeneratedValue
private int id;
private String username;
private String password;
private String email;
But the user never send me the password, so the object built from JSON has "password" field empty. I don't want the password field to be overwritten by a null value. There's a way to say to hibernate "if you find a null value ignore it and don't overwrite the value that is saved in database?". I can't believe that there isn't a easy solution to this apparently simple problem.

I think the source of your problem is that the object you're getting back from your JSON parsing never had the actual values in it. It is a bean that has only the values set that are in your JSON.
You need to load your entity from the DB and then set the non-null fields from your JSON onto the loaded entity. That way only fields that are supplied in the JSON will be set.
I recommend an adapter of some sort to "merge" (not JPA merge) the DB version and the JSON version before saving the DB version.
Adding a #NotNull constraint and Bean Validation will make sure the values are not null when attempting to save. Unfortunately they won't help you get the values into the entity to save.

I have the same issue.
I solved it in this way.
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import java.lang.reflect.Field;
import java.util.Hashtable;
public class Updater {
private final static Logger log = LogManager.getLogger(Updater.class);
public static <E> E updater(E oldEntity, E newEntity) {
Field[] newEntityFields = newEntity.getClass().getDeclaredFields();
Hashtable newHT = fieldsToHT(newEntityFields, newEntity);
Class oldEntityClass = oldEntity.getClass();
Field[] oldEntityFields = oldEntityClass.getDeclaredFields();
for (Field field : oldEntityFields){
field.setAccessible(true);
Object o = newHT.get(field.getName());
if (o != null){
try {
Field f = oldEntityClass.getDeclaredField(field.getName());
f.setAccessible(true);
log.info("setting " + f.getName());
f.set(oldEntity, o);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
return oldEntity;
}
private static Hashtable<String, Object> fieldsToHT(Field[] fields, Object obj){
Hashtable<String,Object> hashtable = new Hashtable<>();
for (Field field: fields){
field.setAccessible(true);
try {
Object retrievedObject = field.get(obj);
if (retrievedObject != null){
log.info("scanning " + field.getName());
hashtable.put(field.getName(), field.get(obj));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return hashtable;
}
}
It is clearly a workaround but it seems to work smoothly... in the next days I think I'll write the recursive part.

Implement setters for you attributes and do the checks there.

Check Hibernate Validation project, which can be used to verify your object on DAO level, as well as on Spring Web layer.

I wrote this answer while being an unexpirienced studen. Today my answer would be similar to the one from #James DW. Also, from the term userDao, I assume that it is some kind of ORM/ODM. In that case it is definitly worth searching "pros and cons of ORM/ODM".
original answer (which was accepted back in 2011):
If your problem is only the database, then I suggest you use a stored procedure, which checks if that value is null, and then dose not change the existing value. That way you can still send a null value, and your validation is on server side which is more robust.

Related

JAVA : How to get the name of variable with annotation in Java?

I am trying to get the name of variable in android using java.
The variable has a annotation, and I want to get the variable's name with the annotation's name. is this possible?
just like this,
#getnameofthisfield
private String name;
use getnameofthisfield and get name
You can do it like this:
Class<YourClass> clazz = // somehow get a reference to the class that contains the field
Field[] fields = clazz.getDeclaredFields();
List<String> fieldNames = new LinkedList<>();
for (Field field : fields) {
if (field.isAnnotationPresent(#getnameofthisfield.class)) {
fieldNames.add(field.getName);
}
}
In the end fieldNames will contain the names of all fields, annotated with #getnameofthisfield.
This comes up when you have a Data holder class that is a model for Firebase fields (for example) and the spelling of the member names must exactly equal the Strings in the Firebase tree. While I have not eliminated the duplicate typing/spelling of the Strings/fields, this will at least detect these programming errors at run-time.
public class User {
private String email;
private String name;
// avoid out-of-sync String names of fields in other files
public static String getFieldName(String fieldRequest) {
try {
return User.class.getDeclaredField(fieldRequest).getName();
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
throw new RuntimeException("Unrecognized field in "
+ User.class.getSimpleName() + ", (" + fieldRequest + ")"); }
}
Here is an example usage:
// demonstration of how the getFieldName() protects against mistakes...
String userNameField = User.getFieldName("name"); // this works
String userEmailField = User.getFieldName("userEmail"); // this throws an error
Get annotation value
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Util{
#SuppressWarnings("unchecked")
public static<T> T getAnnotationValue(Class<?> clazz,Class<? extends Annotation> annotationClass,String element) throws Exception {
Annotation annotation = clazz.getAnnotation(annotationClass);
Method method = annotationClass.getMethod(element,(Class[])null);
if (annotation == null)
return((T)method.getDefaultValue());
return((T)method.invoke(annotation,(Object[])null));
}
}
In my understanding that isnt possible, the java compiler doesn't save variable names. What is it that your trying to do with such name?

Design pattern to use for Null check through Reflection

What we are doing - Annotation driven null and empty values check for Object.
How we are doing - Creating one annotation and putting that annotation on the variable declaration.
I am not sure what design pattern i need to use to make it work best
. Please suggest.
Annotation class -
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
/**
* Message.
*
* #return the string
*/
public String message();
}
How we are declaring variable -
#Test(message="five message")
private String five;
How i am calling annotation processor -
Class<?> annotationClass = annotationTestinClass.getClass();
Field[] decalaredFieldsArray = annotationClass.getDeclaredFields();
List<String> lstString = new ArrayList<>();
parseFields(decalaredFieldsArray,annotationTestinClass,lstString);
How i am processing object -
public static List<String> parseFields(Field[] decalaredFieldsArray,Object obj,List<String> lstString){
Arrays.stream(decalaredFieldsArray).forEach(field ->{
field.setAccessible(true);
Test test = field.getDeclaredAnnotation(Test.class);
if(field.getType().isPrimitive() || field.getType().getName().equals("java.lang.String")){
if(field.isAnnotationPresent(Test.class)){
try {
System.out.println("field value is :"+field.get(obj));
System.out.println("field Name is :"+field.getName());
if(field.get(obj)== null || !StringUtils.isNoneBlank(field.get(obj).toString())){
lstString.add(test.message());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}else{
Field[] objectFields =null;
Object objValue = null;
try {
if(field.isAnnotationPresent(Test.class)){
objValue = field.get(obj);
if(objValue!=null){
objectFields = objValue.getClass().getDeclaredFields();
parseFields(objectFields, objValue, lstString);
}else{
System.out.println("Object value is -"+field.get(obj));
System.out.println("Messsage value is -"+test.message());
lstString.add(test.message());
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
return lstString;
}
Here Test.Class is repersenting my Test annotation.
Annotations don't work that way.
You can create custom annotations that get processed at compile time. But at compile time, very often, you might not be able to check that
#NotNull
private Whatever foo = someBar();
really leads to "not null". In other words: at compile time, no code behind #NotNull can really decide in all cases if the annotated variable will be really not null.
And at runtime, annotations ... are just that: Meta-Information!
Meaning: if you want annotations to have an effect at runtime, you need code that checks for annotations when doing things.
Example: you create #Persist.
And then you have a framework that "processes objects". And whenever that framework processes some object, it can check if that annotation is present, and if so "persist" that object.
In other words:
foo = bar;
will just assign bar to foo; and there is "no framework" in place that could check if foo is #NotNull annotated, and do something about that assignment if bar is null.
So, in order for you to be helpful, you need
That new annotation
Some sort of "processor" that works on objects; and checks if they are annotated; and if they are annotated, special things happen.

Inherited class doesn't get stored in Windows Azure Mobile Service

I am in a process of creating a library for Windows Azure. So, here is a simple generic method to insert a new record:
public <TEntity extends SyncableBase> void addRemoteItem(TEntity itemToAdd) {
MobileServiceTable<TEntity> remoteTable = (MobileServiceTable<TEntity>)mobileServiceClient.getTable(itemToAdd.getClass());
Gson gson = new Gson();
String json = gson.toJson(itemToAdd);
remoteTable.insert(itemToAdd, new TableOperationCallback<TEntity>() {
public void onCompleted(TEntity entity, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
Log.e("SuccessMe", "Success");
// Insert succeeded
}
else {
Log.e("SuccessMe", "Nah "+ exception.getMessage());
// Insert failed
}
}
});
}
Now, here is my SyncableBase class:
public class SyncableBase {
#SerializedName("Bingo")
private int localId;
//#SerializedName("id")
private String remoteId;
private boolean isDeleted;
}
And my ToDoItem class:
public class ToDoItem extends SyncableBase {
private String name;
}
Now, the problem is: This fails with Error processing request. But if I don't extend ToDoItem from SyncableBase and move all those members directly to the former, everything works just fine. As can be seen, I tried serializing my Java object just before calling inset. The serialized json is exactly the same in both the cases. What am I doing wrong?
After days of debugging, I have come up with a potential problem and it's definite solution. This holds valid for the Android Azure SDK valid at the time of writing this. A couple of notes:
For seamless transactions, the id member must be present in the inherited class and not the super class. While validating the object, Azure SDK uses reflection and tries to find a filed with name (or serialized name) equal to id or Id. Somehow, the member isn't found if it is present in super class and we get error.
GSON (the thing which serializes Java object to JSON) is configured inside SDK so that it serializes even the null members. So, when there are no columns in WAMS table (fresh table) and try to insert an item with null fields, the error is thrown. The filed must hold a value so that the type of corresponding column to be generated can be determined. A new field with null value will give you an error.
Here's an example of an item being put in a fresh table.
{
"id": "Awesome unique id",
"name": Beautiful Wallpaper",
"description": null
}
Here, the WAMS would know that it has to generate a column called description; however, it wouldn't know the type of this column. Hence, first object must always have non-null values.
In my particular case, both the problems are there. Taking care of these things solved them.

JPA: Map invalid database values to enums

In my datamodel a have many entities where attributes are mapped to enumerations like this:
#Enumerated(EnumType.STRING)
private MySpecialEnum enumValue;
MySpecialEnum defines some fixed values. The mapping works fine and if the database holds a NULL-value for a column I get NULL in the enumValue-attribute too.
The problem is, that my backend module (where I have no influence on) uses spaces in CHAR-columns to identify that no value is set. So I get an IllegalArgumentException instead of a NULL-value.
So my question is: Is there a JPA-Event where I can change the value read from the database before mapping to the enum-attribute?
For the write-access there is the #PrePersist where I can change Null-values to spaces. I know there is the #PostLoad-event, but this is handled after mapping.
Btw: I am using OpenJpa shipped within WebSphere Application Server.
You could map the enum-type field as #Transient (it will not be persisted) and map another field directly as String, synchronizing them in #PostLoad:
#Transient
private MyEnum fieldProxy;
private String fieldDB;
#PostLoad
public void postLoad() {
if (" ".equals(fieldDB))
fieldProxy = null;
else
fieldProxy = MyEnum.valueOf(fieldDB);
}
Use get/setFieldProxy() in your Java code.
As for synchronizing the other way, I'd do it in a setter, not in a #PreUpdate, as changes to #Transient fields probably do not mark the entity as modified and the update operation might not be triggered (I'm not sure of this):
public void setFieldProxy(MyEnum value) {
fieldProxy = value;
if (fieldProxy == null)
fieldDB = " ";
else
fieldDB = value.name();
}
OpenJPA offers #Externalizer and #Factory to handle "special" database values.
See this: http://ci.apache.org/projects/openjpa/2.0.x/manual/manual.html#ref_guide_pc_extern_values
You might end up with something like this: not tested...
#Factory("MyClass.mySpecialEnumFactory")
private MySpecialEnum special;
...
public static MySpecialEnum mySpecialEnumFactory(String external) {
if(StringUtils.isBlank(external) return null; // or why not MySpecialEnum.NONE;
return MySpecialEnum.valueOf(external);
}

JPA ID Generation Strategy

I defined a generator for a JPA class:
<sequence-generator name="MY_SEQ" allocation-size="-1"
sequence-name="MY_SEQ"
initial-value="100000000" />
There are cases where I already have an ID for an entity but when I insert the Entity the Id gets generated using the generator.
Is it possible to define a generator that will only generate an Id when one does not exist?
I am using Hibernate as a JPA Provider.
Thank you
I couldn't find a way to do this in JPA so I used Hibernate EJB3 event listeners. I over rode the saveWithGeneratedId to use reflection to check the entity for an #Id annotation and then to check that field for a value. If it has a value then I call saveWithRequestedId instead. Other wise I let it generate the Id. This worked well because I can still use the sequence for Hibernate that is set up if I need an Id. The reflection might add overhead so I might change it a little. I was thinking of having a getId() or getPK() method in all entities so I don't have to search for which field is the #Id.
Before I used reflection I tried calling session.getIdentifier(entity) to check but I was getting TransientObjectException( "The instance was not associated with this session" ). I couldn;t figure out how to get the Entity into the session without saving it first so I gave up. Below is the listener code I wrote.
public class MergeListener extends org.hibernate.ejb.event.EJB3MergeEventListener
{
#Override
protected Serializable saveWithGeneratedId(Object entity, String entityName, Object anything, EventSource source, boolean requiresImmediateIdAccess) {
Integer id = null;
Field[] declaredFields = entity.getClass().getDeclaredFields();
for (Field field : declaredFields) {
Id annotation = field.getAnnotation(javax.persistence.Id.class);
if(annotation!=null) {
try {
Method method = entity.getClass().getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
Object invoke = method.invoke(entity);
id = (Integer)invoke;
} catch (Exception ex) {
//something failed (method not found..etc) , keep going anyway
}
break;
}
}
if(id == null ||
id == 0) {
return super.saveWithGeneratedId(entity, entityName, anything, source, requiresImmediateIdAccess);
} else {
return super.saveWithRequestedId(entity, id, entityName, anything, source);
}
}
}
I then had to add the listener to my persistence.xml
<property name="hibernate.ejb.event.merge" value="my.package.MergeListener"/>
it's not a good Idea, sequences are used for surrogate keys, are meaningless in the business sense but assures you, there won't be duplicates thus no error at inserting time.

Categories