How to refresh updated entity data without restarting the server - java

I am using EclipseLink JPA as ORM and web logic 10.3 as an application server in my project. Everything working fine until i got a bug for data refresh. Here is the case one of my entity table row is updated to new value but my entity manager or JPA did not pick that value. For this we have lite rely re started the server. then it picked up the value.
Here is my persistence.xml file and here is the way i am using entity manager in my class.
<persistence-unit name="BasePersistenceUnit" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/CTH_DS</jta-data-source>
<class>org.test.partyrequest.model.dataobject.RqstTrc</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10" />
<!-- Logging level is set to INFO, Need to change in Production -->
<property name="eclipselink.logging.level" value="FINE" />
<property name="eclipselink.persistence-context.flush-mode" value="COMMIT" />
<property name="eclipselink.persistence-context.close-on-commit" value="true" />
<property name="eclipselink.cache.shared.default" value="false" />
</properties>
</persistence-unit>
SPRING JPA XML FILE
<context:load-time-weaver aspectj-weaving="on" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="BasePersistenceUnit" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" />
<bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" />
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!-- Instruct Spring to perform declarative transaction management automatically on annotated classes. -->
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<!-- Post-processor to perform exception translation on #Repository classes
(from native exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
My Entity calss
#Entity
#Table(name = "PRTY_RQST")
public class PrtyRqst implements Serializable {
private static final long serialVersionUID = -4679712398918736694L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PRTY_RQST_PRTYRQSTID_GENERATOR")
#SequenceGenerator(name = "PRTY_RQST_PRTYRQSTID_GENERATOR", allocationSize = 1, sequenceName = "PRTY_RQST_SEQ")
#Column(name = "PRTY_RQST_ID")
private Long prtyRqstId;
#Column(name = "CHLD_RQST_IND")
private String chldRqstInd;
#Column(name = "PARNT_PRTY_RQST_ID")
private BigDecimal parntPrtyRqstId;
#Column(name = "PROCES_REFR")
private String procesRefr;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "RQST_DT_TM")
private Date rqstDtTm;
#Column(name = "UPDT_BY")
private String updtBy;
// bi-directional many-to-one association to PrtyKey
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<PrtyKey> prtyKeys;
// bi-directional many-to-one association to PrtyRqstHist
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#OrderBy("rqstDtTm DESC")
private List<PrtyRqstHist> prtyRqstHists;
#OneToOne(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private RqstPayload rqstPayload;
// bi-directional many-to-one association to RqstTrc
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<RqstTrc> rqstTrcs;
// bi-directional many-to-one association to AddtnRqstInfo
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<AddtnRqstInfo> addtnRqstInfos;
// bi-directional many-to-one association to BusnApplc
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "BUSN_APPLC_ID")
private BusnApplc busnApplc;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "INTN_PROCES_TYP_ID")
private IntnProcesTyp intnProcesTyp;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "INTN_STATS_ID")
private IntnStat intnStat;
#Column(name = "ORCHESTRATION_ID")
private String orchestrationId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "ALLW_CHNL_ID")
private AllwChnl allwChnl;
// bi-directional many-to-one association to RqstTyp
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "RQST_TYP_ID")
private RqstTyp rqstTyp;
#Column(name = "TRACK_RQST_IND")
private String trackRqstInd;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "SUBMIT_DT_TM")
private Date submitDtTm;
#Temporal(TemporalType.DATE)
#Column(name = "EFFECTIVE_DT")
private Date effectiveDt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "RQST_CREATE_DT_TM")
private Date rqstCreateDtTm;
In my DAO IMPL class I have this.persist(prtyRqstDO);
#Transactional(readOnly = true, propagation=Propagation.REQUIRED)
private PartyRequestBO createRequest(PartyRequestBO partyRequestBO, boolean isParent) throws RuntimeException {
if (log.isDebugEnabled()) {
log.debug("Enter: PartyRequestsDAOImpl:createRequest()");
}
partyRequestBO.setOrchestrationID(generateOrchestrationId());
PrtyRqst prtyRqstDO = PartyRequestEntityMapper.partyRequestMapper(partyRequestBO, isParent, true);
try {
this.persist(prtyRqstDO);
partyRequestBO.setRequestIdentifier(prtyRqstDO.getPrtyRqstId());
} catch (Exception e) {
if(log.isDebugEnabled()) {
log.debug("PartyRequestsDAOImpl:createRequest : " + PartyRequestConstants.UNABLE_TO_INSERT, e);
}
throw new PartyRequestDataException(PartyRequestConstants.UNABLE_TO_INSERT, e);
}
if (log.isDebugEnabled()) {
log.debug("Exit: PartyRequestsDAOImpl:createRequest()");
}
return partyRequestBO;
}
#Transactional(readOnly = true, propagation=Propagation.REQUIRED)
public void persist(T entity) {
if (log.isDebugEnabled()) {
log.debug("Enter: BaseDAO:persist() : " + entity);
}
this.getEntityManager().persist(entity);
if (log.isDebugEnabled()) {
log.debug("Exit: BaseDAO:persist()");
}
}
public EntityManager getEntityManager() {
if (log.isDebugEnabled()) {
log.debug("Enter: BaseDAO:getEntityManager() : " + this.entityManager);
}
return this.entityManager;
}
Here the problem is, if i update the row in one of the table through back end my application container is not picking the change.
Can any one Tell me? thank you in advance.
EDIT:
Thank you both of you. I have modified according to your comments added following lines of code.
this.entityManager.clear();
this.entityManager.close();
//this.getEntityManager().refresh(entityManager);
Here i could able to get the update value what i have done it through backend with out restarting server. But the problem is it hold all the changed values.
for example i have changed value to FulOrderWSA it was working. changed to FulorderWSB it was working again.Now i have tried for FulOrderWSZ it didn't work(DB values is FulorderWSB ).
Finally i tried here with old value that is FulorderWSA as per DB it should not work but it worked for me. what i noticed that it is holding all the DB changed values here.
How to get ride of this. I have used both clear and close for entityManager. can any one help me on this.
thank you.
Vijay.

You have turned off the EclipseLink shared cache (AKA second level cache), so the issue is likely that you are holding onto long lived EntityManagers. Once an entity becomes managed by the EM, JPA requires that EM to return that exact instance from every find/query operation as it was when first read and any changes your app might have made to it.
There are a number of options. The best is to look at your EntityManager lifecycle and only obtain an EntityManager when needed, and close it when done. Or, just call em.clear() at points to prevent them from filling up, which will detach all entities associated to the em. Make sure to flush changes though if you wish to keep the changes before calling clear.
If there is a specific entity you need to refresh, em.refresh(entity) will work. This will clear any changes the application might have made though, and can be dangerous with cascade refresh settings mixed with lazy access - so use carefully or you may unintentionally wipe out changes to a whole tree at a later time.

You have caching disabled, so you should see any database changes.
My guess is that you are hold onto a single EntityManager in your DAO. This is very bad, as an EntityManger should be created per transaction, or per request, not held for the duration of the application. It is also not thread safe, so holding onto a single one does not make sense, as it is a transactional object.
You seem to also be using Spring, so it might be proxying the EntityManager underneath and creating one per transaction, but perhaps you have not configured Spring or your transactions correctly.
Include the code the creating/configures the EntityManager.

Thanks,
for all your support. Basically we are not using Cache from EclipseLink. we had bean that handles all the metadata initialization as init method. what we have done is, used JDK Timer to reload the particular Bean to refresh the data. It was working fine.
I have checked the time taking to refresh the all the methods are less than 500 milliseconds. I can foresee only issue when this thread is executing and there is a request.since it is taking less than 500 millisecs its ok for us.
I hope this will be helpful if someone is not using cache you can try this approach.
Thank you.

Related

Layz loaded list gets reset upon detach from EntityManager

I have lazy 1:n relation from Bp to BpHistorisiert.
To load a Bp including the related BpHistorisiert id do
Bp bp= entityManager.find(Bp.class, anId);
bp.getBpHistorisiertList();
This works fine, the call to getBpHistorisiertList() loads the n side lazy as expected. However when bp gets detached, the bpHistorisiert List in bp is explicitely reset to null. I can see in the debugger that this is explicitely done by the detach funcionality of the OpenJPA EntityManager.
So my question is: How to load lazy relations and keep the values when I work with detached entities?
Bp
#Entity
#Table(name = "BP", schema = "INFOP_STAMMDATEN")
public class Bp extends BaseEntity implements EntityId, Serializable {
/** technische ID */
#Id
#Column(name = ID)
private Long id;
#Valid
#OneToMany(mappedBy = "bp", orphanRemoval = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<BpHistorisiert> bpHistorisiertList = new ArrayList<>();
}
BpHistorisiert
#Entity
#Table(name = "BP_HISTORISIERT", schema = "INFOP_STAMMDATEN")
public class BpHistorisiert implements EntityId, GueltigkeitOwner, AbkuerzungOwner, Serializable {
#Id
#Column(name = ID)
private Long id;
#NotNull
#ManyToOne
#JoinColumn(name = BP_ID)
#ForeignKey
private Bp bp;
}
The answer is: by reporting an issue on OpenJPA issue tracker and waiting for resolution.
According to the docs, you should not observe the behavior you described.
Thanks to #crizzis answer I found the reason for this behaviour. We actually have set
<property name="openjpa.DetachState" value="fetch-groups(DetachedStateField=true)"/>
in our persistence.xml. The consequence is, that all lazy fileds of all entities are set to the java default value up on detach. For a lazy List this is null.
So yes, this is an OpenJPA feature, not a bug.
And after all this is even explizitely documented: https://issues.apache.org/jira/browse/OPENJPA-1913?attachmentSortBy=fileName

OpenJPA - Nested OneToMany relationships merge issue

Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936

JPA - EntityManager find method returns duplicates

I have entity with two Lists of children entities. When calling EntityManager.find() it returns duplicates based on multiplication of two lists.
I'm using Spring, Jackson and JPA with Hibernate and SQL Server.
When testing with parent that has N elements of first and M of second child entity it always returns N*M elements of both entities.
For example below there are 3 tasks and 5 comments and JPA returns 15 for both lists. (5 copies of task list, and 3 copies of comment list)
Output from controller is:
Comments 15
Tasks 15
And the rest of the code is below.
controller.java
#RequestMapping(method = RequestMethod.GET)
public String listAll(Model model) {
Goal goal = new Goal();
goal = service.getGoalById(25);
System.out.println("Comments " + goal.getComments().size());
System.out.println("Tasks " + goal.getTasks().size());
return "home";
}
service.java
#Transactional
public Goal getGoalById(int goalId) {
Goal goal = em.find(Goal.class, goalId);
return goal;
}
goal.java
#Entity
#Table(name = "goal")
public class Goal {
#Id
#GeneratedValue
private int id;
#JsonManagedReference
#OneToMany(mappedBy = "tasksGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Task> projectTasks = new ArrayList<Task>();
#JsonManagedReference
#OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Comment> goalComments = new ArrayList<Comment>();
...
}
task.java
#Entity
#Table(name="projectTask")
public class Task {
#Id
#GeneratedValue
private int id;
#JsonBackReference
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "task_goal_id")
private Goal tasksGoal;
...
}
comment.java
#Entity
#Table (name="goalComment")
public class Comment {
#Id
#GeneratedValue
private int id;
#JsonBackReference
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "goal_id")
private Goal commentsGoal;
...
}
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2008Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
<property name="hibernate.connection.charSet" value="UTF-8" />
<!-- Hibernate prints SQL -->
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
This is because of your fetch = FetchType.EAGER.
he Hibernate tries to get everything in one shot, i.e. using one single query for each entry of element linked to a "master" object.
Hibernate creates JOINs and tries to get all your collections with one query.
This problem can be successfully solved at a cost of N+1 query, if you add the #Fetch (FetchMode.SELECT) annotation to your collection.
If you really need FetchType.EAGER and don't want to replace your collection with Set you can use #Fetch (FetchMode.SELECT) annotation for your collection:
#JsonManagedReference
#OneToMany(mappedBy = "tasksGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#Fetch (FetchMode.SELECT)
private List projectTasks = new ArrayList();
#JsonManagedReference
#OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#Fetch (FetchMode.SELECT)
private List<Comment> goalComments = new ArrayList<Comment>();
#Fetch (FetchMode.SELECT)
can help you. it is the default fetching strategy. it enabled the lazy loading of all it’s related collections.

Polymorphic association jpa2 hibernate

I think im doing something wrong but i cant get working #any annotation on hibernate 4.2.2 with jpa2 1.0.1
The class works ok, but i cant get join.
My code is this:
#Entity(name = "conta")
public class Conta {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "empresa_id")
private int empresaId;
private String descricao;
#Any(metaColumn = #Column(name = "tipo"), fetch = FetchType.EAGER)
#AnyMetaDef(idType = "int", metaType = "string", metaValues = {
#MetaValue(value = "BANCO", targetEntity = Banco.class),
#MetaValue(value = "CIELO", targetEntity = Cielo.class)
})
#JoinColumn(name = "financeira_id")
private Financeira financeira;
//getters and setters
}
Financeira its an interface, Banco.class and Cielo.class implements Conta as well. When i try to fetch from DB i can get all records, but the join doesnt happen.
My code inspection on Intellij IDEA says for private Financeira financeira that "'Basic' attribute type should not be 'Financeira'". I know or i think i know that this message says that jpa dont have metamodel for #any annotation, but hibernate does, so, this should work right?
UPDATE
Got the problem solved by using hibernate native xml configuration.
Now i can persist my objects but i got another problem. I cant fetch the association, im using a factory to fetch de association on #postLoad event, but thats not the right way.
above my xml code.
<any id-type="java.lang.Integer" meta-type="string" name="tipo" cascade="all">
<meta-value value="BANCO" class="br.com.leaftecnologia.lfadmin.model.financeiro.BANCO" />
<meta-value value="PLAYSMS" class="br.com.leaftecnologia.lfadmin.model.financeiro.CIELO" />
<column name="tipo" />
<column name="financeira_id" /></any>

java/Spring Hibernate not updating entities

I have read and reread everything here on SO and many other sites but can't seem to figure out why my updated objects are not updating.
The basic overview of what I'm doing:
Service layer asks DAO for some People
Return an ArrayList of People from the DB (DAO / #Repository)
Service Layer manipulates object and adds them to a new arraylist
Service Layers passes new list back to DAO to update
NOTHING GETS Updated
If I throw a log message in my object has the new values, the children are correctly hydrated. I get no errors in the code is just doesn't commit my changes.
Here is some code:
PersonDAO annotated as #Repository
public void updatePeople(List<Person> people) {
log.info("Updating " + people.size() + " people");
try {
Transaction tx = getCurrentSession().beginTransaction();
for (Person person : people){
getCurrentSession().saveOrUpdate(person);
}
getCurrentSession().flush();
tx.commit();
getCurrentSession().close();
} catch (Exception e){
log.error("Exception Updating all people " + e.toString());
e.printStackTrace();
}
}
public List<Person> getAssociatesByStoreId(String storeId) {
try {
List<Person> usersInStore = (List<Person>) getCurrentSession()
.createCriteria(Person.class).createCriteria("store")
.add(Restrictions.eq("storeId", storeId)).list();
return usersInStore;
} catch (Exception e) {
log.error("exception in getAssociatesByStoreId ", e);
}
return null;
}
PersonService annotated as #Service - relevant method
/* I put the people into a map to do some other logic on them */
for (Person person : personDAO.getAllPeople()){
personMap.put(person.getEmployeeId() + "-" + person.getStore().getStoreId(), person);
}
/*
I iterate a list creating new Person objects (based on some random stuff),
including saving any children entities (managementcodes and stores) that need to be created
After I have created a new Person I attempt to find it in the map from above.
If I find it pull it out of the map and put it into an array list
that is eventually passed back into the DAO
*/
Person annotated as #Entity
private int personid;
private String firstName;
private String lastName;
private Store store;
private ManagementCode managementCode;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "personid", unique = true, nullable = false)
public int getPersonid() {
return this.personid;
}
/*a bunch of getters and setters*/
#ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#org.hibernate.annotations.Cascade( {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST})
#LazyCollection(LazyCollectionOption.EXTRA)
#JoinColumn(name = "managementlevel", nullable = true)
public ManagementCode getManagementCode() {
return this.managementCode;
}
#ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
// #org.hibernate.annotations.Cascade( {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST})
#JoinColumn(name = "storeid", nullable = false)
public Store getStore() {
return this.store;
}
Store annotated as entity (Managementcode is the same)
/*fields + getters and setter */
#OneToMany(fetch = FetchType.LAZY, mappedBy = "store", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#LazyCollection(LazyCollectionOption.EXTRA)
#JsonIgnore
public Set<Person> getPersons() {
return this.persons;
}
EDIT I added both sets of Cascade type annotations above and still no luck
EDIT 2 Updated the to show the primary key definition
Someone please help me before I kick a puppy.
Thanks
There are three main types of entity objects with JPA:
Managed. These are the objects that are cached by JPA-provider, so the JPA-provider keeps track of their states, and commits any changes made to them when the transaction commits. These are the objects you get when you query. They have a database id.
New. These are entity objects that are not yet persisted in the database. The JPA-provider knows nothing about them. They do not have a database id.
Detached. These are objects that used to be managed, but no longer are. Typically, objects that are returned to a method that is outside the transaction. These object have a database id, but they are not part of the cache, so the JPA-provider does not know them.
In order to persist a relationship between an entity and another, the referenced entity has to managed. There are multiple ways of achieving this.
Cascade. Cascade from entity A to entity B means that when a cascading operation is performed on entity A by you in your code, the JPA-provider will perform the same operation on entity B. If you add CascadeType.MERGE to the relationship, and have entity A reference a detached entity B, then doing a merge() on entity A will first to a merge() on entity B, making it managed. When entity A is then stored, it has a reference to a managed B, and the relationship is persisted. (If you also want to persist references to objects that are not already in the database, add CascadeType.PERSIST as well.)
Making sure you only reference managed objects. Inside your transaction, load the referenced objects (the managed ones) and replace the references with managed objects, before storing.
Widen your transaction. That way, the referenced objects will never become detached.
It could be the case that you need to have "cascade = {CascadeType.MERGE, CascadeType.PERSIST}" on the ManyToOne annotation. The default behavior does not cascade anything to the associated objects.
/**
* (Optional) The operations that must be cascaded to
* the target of the association.
*
* <p> By default no operations are cascaded.
*/
CascadeType[] cascade() default {};
Eg.
#ManyToOne(optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = EAGER)
UPDATE:
In case you are using spring managed transaction manager and have the liberty to use annotation driven transaction demarcation, you can try the spring #Transactional annotation instead of manually starting transaction and committing/flushing.
I have following setup:
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="local-dataSource"/>
<property name="packagesToScan" value="X"/>
<property name="persistenceXmlLocation" value="classpath:persistence.xml"/>
</bean>
DAO class:
#Repository
public class EventRepository {
#PersistenceContext
EntityManager entityManager;
#Transactional
public void persist(Event event) {
entityManager.persist(event);
}
}
First of all, make sure that you use the fitting CascadeType on those ManyToOne annotations.
Secondly, a merge Operation in the entityManager/session does not remain object equality by reference. That means, in an update case where a merge is performed, check if a different object is returned. You are supposed to always use the return values of save operations.

Categories

Resources