I have a database model class Project, and it has two embedded object fields:
#Embedded
public ContacterInfo contacter = new ContacterInfo();
#Embedded
public CompanyInfo company = new CompanyInfo();
'cause I don't want to check is null every time I use company and contacter, so I decided to create them anyway.
What I expected is, when there's nothing for contacter in database, Java would create a new ContacterInfo for me, and then I can just use it for new data. But in fact, I found contacter still could be set to null. I suspect that JPA load null from database and override my new create object with it.
How can I fix this ?
You can use the JPA Entity listeners (ObjectDB has a good explication). By example:
#PostLoad
void onPostLoad() {
if (contacter == null) {
contacter = new ContacterInfo();
}
if (company == null) {
company = new CompanyInfo();
}
}
Every time JPA load an instance of current entity, onPostLoad will be called.
Good luck!
Related
I have implemented by project using Spring-Data-Rest. I am trying to do an update on an existing record in a table. But when I try to send only a few fields instead of all the fields(present in Entity class) through my request, Spring-Data-Rest thinking I am sending null/empty values. Finally when I go and see the database the fields which I am not sending through my request are overridden with null/empty values. So my understanding is that even though I am not sending these values, spring data rest sees them in the Entity class and sending these values as null/empty. My question here is, is there a way to disable the fields when doing UPDATE that I am not sending through the request. Appreciate you are any help.
Update: I was using PUT method. After reading the comments, I changed it to PATCH and its working perfectly now. Appreciate all the help
Before update, load object from database, using jpa method findById return object call target.
Then copy all fields that not null/empty from object-want-to-update to target, finally save the target object.
This is code example:
public void update(Object objectWantToUpdate) {
Object target = repository.findById(objectWantToUpdate.getId());
copyNonNullProperties(objectWantToUpdate, target);
repository.save(target);
}
public void copyNonNullProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
}
public String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] propDesList = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(PropertyDescriptor propDesc : propDesList) {
Object srcValue = src.getPropertyValue(propDesc.getName());
if (srcValue == null) {
emptyNames.add(propDesc.getName());
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
You can write custom update query which updates only particular fields:
#Override
public void saveManager(Manager manager) {
Query query = sessionFactory.getCurrentSession().createQuery("update Manager set username = :username, password = :password where id = :id");
query.setParameter("username", manager.getUsername());
query.setParameter("password", manager.getPassword());
query.setParameter("id", manager.getId());
query.executeUpdate();
}
As some of the comments pointed out using PATCH instead of PUT resolved the issue. Appreciate all the inputs. The following is from Spring Data Rest Documentation:
"The PUT method replaces the state of the target resource with the supplied request body.
The PATCH method is similar to the PUT method but partially updates the resources state."
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.hiding-repository-crud-methods
Also, I like #Tran Quoc Vu answer but not implementing it for now since I dont have to use custom controller. If there is some logic(ex: validation) involved when updating the entity, I am in favor of using the custom controller.
I got an exception while trying to insert new object.
Logcat:
java.lang.IllegalStateException: Source entity has no ID (should have been put before)
at io.objectbox.relation.ToMany.internalCheckApplyToDbRequired(ToMany.java:599)
Insert function:
public static void setSyncData(long contactId, SyncerData syncData) {
final Box<SyncerData> box = getObjectBoxStore().boxFor(SyncerData.class);
SyncerData syncerData = box.query().equal(SyncerData_.id, contactId).build().findFirst();
if (syncerData == null) {
syncerData = new SyncerData();
syncerData.setPhoneOrIdKey(ContactData.generateId(Phone.EMPTY, contactId));
}
syncerData.setSyncerDetailsToMany(syncData.getSyncerDetailsToMany());
box.put(syncerData);
}
What that's mean, that I can't put new object ToMany list before I added to object?
I think it's related to https://github.com/objectbox/objectbox-java/issues/104.
In essence setting a relation to a new plain List is somewhat problematic because ObjectBox lacks the change tracking, which is available in the ToManyclass. We'll look how to sync that in a future release.
Please try something like the following:
toMany.clear();
toMany.addAll(newList);
I try to use objectify transaction, but I have some issues when I need to reload an object created in the same transaction.
Take this sample code
#Entity
public class MyObject
{
#Parent
Key<ParentClass> parent;
#Index
String foo;
}
ofy().transact(new VoidWork()
{
#Override
public void vrun()
{
ParentClass parent = load();// load the parent
String fooValue = "bar";
Key<ParentClass> parentKey = Key.create(ParentClass.class, parent.getId())
MyObject myObject = new MyObject(parentKey);
myObject.setFoo(fooValue);
ofy().save().entity(myObject).now();
MyObject reloaded = ofy().load().type(MyObject.class).ancestor(parentKey).filter("foo", fooValue).first().now();
if(reloaded == null)
{
throw new RuntimeException("error");
}
}
});
My object reloaded is always null, maybe I miss something, but logically within a transaction I can query an object which was created in the same transaction?
Thanks
Cloud Datastore differs from relational databases in this particular case. The documentation states that -
Unlike with most databases, queries and gets inside a Cloud Datastore
transaction do not see the results of previous writes inside that
transaction. Specifically, if an entity is modified or deleted within
a transaction, a query or lookup returns the original version of the
entity as of the beginning of the transaction, or nothing if the
entity did not exist then.
https://cloud.google.com/datastore/docs/concepts/transactions#isolation_and_consistency
(This is a simplification of the real problem)
Let's start with the following little class:
#Entity
class Test {
Test(int id, String name) {
this.id = id;
this.name = name;
}
#Id
private int id;
#Column
private String name;
#Override
public int hashCode() {
return id;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Test) {
return id == ((Test) obj).id;
}
return false;
}
}
If we execute the following, no exception occurs:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1); // <-- No exception
tA.commit();
I guess the second call is ignored, or maybe the object is saved to the DB again. The thing is there is no problem in saving the same entity twice. Now let's try the following:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
Test obj1_ = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1_); // <-- javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session
tA.commit();
What? How could it possibly be relevant that the object is in a different memory location? Somehow it is and the code throws an exception.
How can I make the second example work just like the first?
I am just rewriting what #jb-nizet wrote in the comments, which feels like the answer to me:
Hibernate doesn't use ==. It simply does what you're telling it to do.
persist's contract is: associate this object with the session. If it's
already associated to the session, it's a noop. If it isn't, it is
associated to the session to be inserted in the database later. If
what yo want to do is make sure the state of this object is copied to
a persistent entity, and give me back that persistent entity, then
you're looking for merge().
So the solution was to just use
AtheManager.merge(obj1);
instead of
AtheManager.persist(obj1);
In first case, you save the same object twice, which is allowed.
But in second case, you save two different object to database, but both has the same primary key. It is database constraint violation.
In the first example you pass a reference to an object to save it and in the second call you pass exactly the same reference; they both point to the same object in memory.
However, in the second example you allocated two objects with two new calls which creates the objects at two different memory addresses; they are two different objects. The first reference points to some other memory address then the second object's reference. If you tried this in the second example it would return false: obj1 == obj1_
I have an JPA+Hibernate entity that I need to send via RMI to a client that doesn't know Hibernate, so I've made a method to "cleanse" Hibernate from it:
// shortened
public class Player {
private Set<Item> ownedItems;
public void makeSerializable() {
ownedItems = new HashSet<Item>(ownedItems);
}
}
However, when I call makeSerializable Hibernate will attempt to lazy-load ownedItems if it's not loaded yet, which I don't want, and which is also impossible because there is Hibernate session. Instead, if ownedItems is not loaded, I'd like to set it to null or an empty set.
How can I do that?
if (!Hibernate.isInitialized(ownedItems)) {
ownedItems = new HashSet<Item>();
}
This is the way to test if a collection is initialized without the need for a session.