I have a User entity:
@Entity
@Table( name = "bi_user" )
@SequenceGenerator( name = "USER_SEQ_GEN", sequenceName = "USER_SEQUENCE" )
public class User
extends DataObjectAbstract<Long>
{
private static final long serialVersionUID = -7870157016168718980L;
/**
* key for this instance. Should be managed by JPA provider.
*/
@Id
@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "USER_SEQ_GEN" )
private Long key;
/**
* Username the user will use to login. This should be an email address
*/
@Column( nullable=false, unique=true)
private String username;
// etc. other columns and getters/setters
}
Where DataObjectAbstract is a simple @MappedSuperClass
that has a jpa version and equals/hashcode definition.
I have a base dao class that looks like this
public abstract class BaseDaoAbstract<T extends DataObject<K>, K extends Serializable>
implements BaseDao<T, K>
{
@PersistenceContext
private EntityManager em;
/**
* Save a new entity. If the entity has already been persisted, then merge
* should be called instead.
*
* @param entity The transient entity to be saved.
* @return The persisted transient entity.
*/
@Transactional
public T persist( T entity )
{
em.persist( entity );
return entity;
}
/**
* merge the changes in this detached object into the current persistent
* context and write through to the database. This should be called to save
* entities that already exist in the database.
*
* @param entity The entity to be merged
* @return The merged entity.
*/
@Transactional
public T merge( T entity )
{
return em.merge( entity );
}
// other methods like persist, delete, refresh, findByKey that all delegate to em.
}
I have defined the OpenEntityManagerInView filter in web.xml as follows
<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>biEmf</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I recently upgrade to eclipselink 2.3.2 and Spring 3.1 and converted from CGLIB proxies to Load Time Weaving with aspectJ for Spring but I did not configure LTW for eclipselink.
The problem is in this code which lives in a spring ApplicationListener, see comments.
User user = userService.findByKey(userDetails.getKey());
// THIS MERGE NEVER WRITES THROUGH TO THE DATABASE.
// THIS DOESN'T WORK AS PERSIST EITHER
user = userService.merge( user.loginSuccess() );
user.loginSuccess just sets some fields and returns this
I'm certain it's getting through the code because I get log statements around it and I can set a breakpoint and walk through it. My postgres log doesn't show any traffic getting to postgres for the merge.
I am saving other stuff all over the place without issue, including users in another location when they change their password, and I know this code used to work. Is there something obviously amiss here? Am I using the OpenEntityManagerInViewFilter incorrectly? Should I need to be in a @Transactional method in order for the entities to be considered managed? Any help is appreciated.
Update I tried the flush as suggested by prajeesh. Here's the code
@Transactional
public T merge( T entity )
{
entity = em.merge( entity );
em.flush();
return entity;
}
in a class in com.bi.data
. I have this in my spring app config file
<context:component-scan base-package="com.bi.controller,com.bi.data,com.bi.web" />
In my spring configuration I have
<context:load-time-weaver/>
<tx:annotation-driven mode="aspectj"/>
with an aop.xml that looks like this:
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.bi..*"/>
</weaver>
</aspectj>
and I got a
javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
So something is obviously misconfigured, but what?
Update 2: I reverted my changes to enable load time weaving and now the merge goes through with or without the flush, but I still don't understand what the problem is with LTW...
Merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again).
If you don't close it your entities will be kept as attached, even after you're done using them. Your context will be kept alive even when you can no longer access your EM. The JPA Specification contains more details.
JPA's merge method copies the state of a detached entity to a managed instance of the same entity. Hibernate, therefore, executes an SQL SELECT statement to retrieve a managed entity from the database.
Persist should be called only on new entities, while merge is meant to reattach detached entities. If you're using the assigned generator, using merge instead of persist can cause a redundant SQL statement.
Try the em.flush()
also after the em.merge()
. Sometimes the EntityManager
just keeps the the changes for updating later.
EntityManager autocommit is false. Please use transaction like below;
EntityManager em = null;
EntityTransaction t = null;
try {
em = emf.createEntityManager();
t = em.getTransaction();
t.begin();
em.merge(myTestObject);
t.commit();
} catch (Exception e) {
t.rollback();
throw new RuntimeException(e.getMessage());
}finally{
if(em != null)
em.close();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With