best practice for implement ManyToOne - java

I always use the following method to implement ManyToOne in class :
#Column(name = "buyer_id")
private Long buyerId;
#ManyToOne
#JoinColumn(name = "buyer_id", insertable = false, updatable = false)
UserGroup buyer;
However, I have a question whether it is better to use the following code:
#ManyToOne
#JoinColumn(name = "buyer_id", insertable = false, updatable = false)
UserGroup buyer;
or not?
In the first case, I always set the id value obtained in buyerId for saving after saving the UserGroup, but in the second case, I put the userGroup model completely (after save) in my entity then save entity.
I'm not sure if the method I use is the best.
I searched the internet but could not find a technical reason that the latter is not appropriate.

Related

how to address a database column multiple times using hibernate

I have a project to access a database via REST, in which an old database has to be addressed in a modern way using javax.persistence annotations and JPA.
One of my classes fields look like this:
#Column(name = "properties_id", nullable = false)
private int propertiesId;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "properties_id", referencedColumnName = "dbid", insertable = false, updatable = false)
private PropertyList propertyList;
#Id
#Column(name = "propertiesIdx", nullable = false)
private int propertiesIdx;
As you can see in the fields, the column properties_id is used as part of the primary key and as a foreign key. Unfortunately, there is no possibility to change the database layout.
In the application, I need both fields: one for joining the parent-table and another for addressing the property directly.
But in the way it is currently implemented, it doesn't work.
Does somebody have an idea how to address this problem?
For further details of the problem please feel free to visit the git-repo:
https://github.com/SerNet/verinice-rest-service/tree/import

JPA insertable = false updatable = false id not updated on creation

We have 2 entities with a #ManyToOne relationship.
When we create an instance of EntityB within a #Transactional method, entityAId (insertable = false updatable = false), is not updated automatically - even though that the entityA instance was already persisted.
Is there a way around this? Do we have to update it manually in the ctor?
#Entity
public class EntityA {
#Id
#GeneratedValue
private Long id;
public EntityA() {
super();
}
...
}
#Entity
public class EntityB {
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
private EntityA entityA;
#Column(name = "entityA_id", insertable = false, updatable = false)
private Long entityAId;
public EntityB() {
super();
}
public EntityB(EntityA entityA) {
super();
this.entityA = EntityA;
}
...
}
EDIT: Also tried the following, but still entityAId = null within the transaction (even though entityA was persisted before).
#Entity
public class EntityB {
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "entityA_id", insertable = false, updatable = false)
private EntityA entityA;
#Column(name = "entityA_id")
private Long entityAId;
...
}
Hibernate is not going to populate entity fields 'on the fly' (when you change some other fields or similar). It is also not going to do it on persist/flush (exceptions being some special fields like id and version).
Non-insertable/non-updatable fields are populated when entity instances are fetched from the DB. So, to make such fields initialized/refreshed by Hibernate in the same transaction in which you perform changes to the underlying columns they are mapped to, you should first flush the session and then either:
clear the session and re-read the entities;
or, refresh the entities for which you want to reflect such kind of changes.
To update the id field a persist action of the object is required. By default, objects in field entityA are not automatically persisted when persisting an object of EntityB.
I see two possible solutions:
A) Use cascade
#ManyToOne(optional = true, fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
private EntityA entityA;
(or use CascadeType.ALL)
B) Persist entityA manually
entityManager.persist(entityA);
To me your mapping does not look right. #ManyToOne or any other association defined between entities but you have defined it on entityAId. Ideally it should be entity (an here you should use insertable = false updatable = false)and you should have separate field entityAId with #column defined on it. Now you should update this field yourself.
If you want to handle hibernate for you remove insertable = false updatable = false

custom constraint while updating Parent Entity Column in hibernate

For example I have two tables Employee and Department and I am using
#ManyToOne
#JoinColumn(name = "DEPT_ID", nullable = true, insertable = false, updatable = false)
private Department department;
in Employee.
Here I want to add one constraint that, whenever one of the column of Department is updated to some value then it should not get updated if it has child records in Employee.
Can I achieve this by using CascadeType?
Or is there any other way by which I can define custom constraint?
If your intention is to prohibit memory to database sync when Department is updated, then you're done as you have insertable=false, update=false:
#JoinColumn(name = "DEPT_ID", nullable = true, insertable = false, updatable = false)

How to enforce orphan deletion on a ManyToOne relationship

#ManyToOne(fetch = LAZY)
#JoinColumn(name = COL_GROUP_ID, nullable = false, insertable = false, updatable = false, referencedColumnName = COL_ID)
#OnDelete(action = CASCADE)
#Cascade(value = DELETE_ORPHAN)
private Group group;
How to enforce orphan deletion on a ManyToOne relationship, the above code snippet worked for us in Hibernate 3.3.x, but post migration to 3.6.5.Final it shows up as a WARNING in the code. is there a equivalent flag like orphanRemoval = true which is applied on a #OneToMany notation?
You can not apply ORPHAN_REMOVAL to MANY_TO_ONE side.
Suppose you have an entity City which has #OneToMany Citizen and on the other side you have #ManyToOne City in Citizen entity. In your scenario removing one citizen will lead to removing the whole city, thus ORPHAN_REMOVAL is only applicable to XXX_TO_Many side

Jpa query pulling from cache instead of database?

I am hitting an issue with my Hibernate backed Jpa queries returning data that is not up to date. I assume it is an issue with pulling data from the cache instead of the database itself.
For example, I will change and persist an object on one page and then go back to the previous page, which lists rows of the database, and it will show the objects as they existed prior to the change. I can see my query fire from my DAO in my logs and I can go into the database and see that the changes have been persisted, but JPA is not pulling up to date data from the database when I move on to the next page.
I believe there might be some kind of session caching at work, as I will not see an updated database view when I load the page up in another web browser.
How do I fix this issue?
EDIT: I've done some follow up testing, including logging on my controller to make sure my MVC framework (Spring MVC) isn't caching anything. It isn't, even on the controller level it sees out of date database information.
Here is the mapping snippet from my ORM entity file;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = true)
#Column(name = "ID", nullable = false)
private Integer id;
#Basic(optional = false)
#Column(name = "Name", nullable = false, length = 100)
private String name;
#Basic(optional = false)
#Column(name = "DayOffset", nullable = false)
private int dayOffset;
#Basic(optional = false)
#Column(name = "StartTime", nullable = false, length = 5)
private String startTime;
#Basic(optional = false)
#Column(name = "Enabled", nullable = false)
private boolean enabled;
#Basic(optional = false)
#Column(name = "LastTouched", insertable = false, updatable = false, nullable =
false)
#Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;
#Column(name = "TouchedBy", length = 50)
private String touchedBy;
#JoinTable(name = "ReconciliationSearchRule",
joinColumns = {#JoinColumn(name = "ReconciliationId",
referencedColumnName = "ID", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "SearchRuleId",
referencedColumnName = "ID", nullable = false)})
#ManyToMany(fetch = FetchType.LAZY)
private Collection<SearchRule> searchRuleCollection;
#JoinColumn(name = "ServerId", referencedColumnName = "ID", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Server server;
#OneToMany(mappedBy = "reconciliation")
private Collection<Report> reportCollection;
I figured out what was going on. My DAO is being injected as a prototype (aka non-singleton) so the backing EntityManager was being created for each use of the DAO. Database changes external to the EntityManager are not registered by queries to that particular EntityManager.
Of course setting the DAO to a singleton causes other issues with the multi-threaded part of my application, but that is a whole other issue.
There are a couple ways of going about this:
Set an eviction strategy using the #DataCache(timeout = 1000) on the entity in question and set it to something reasonable for the object type you are using.
Call evict(...) on your entity or class. The implementation details of this are specific based upon what provider (e.g. Hibernate) you are using.
For example, I will change and persist an object on one page and then go back to the previous page, which lists rows of the database, and it will show the objects as they existed prior to the change.
Second level cache management should be transparent, you don't have to evict() an entity manually after any update, the 2nd level cache gets invalidated for the target table "automatically" by Hibernate.
So, my question is: what do you mean by "going back to the previous page"? Do you mean using the navigator "Back" button? Did you reload the page? Can you clarify this?
Also, can you post your mapping file or annotated entity?

Categories