Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EntityManager.merge not doing anything

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...

like image 790
digitaljoel Avatar asked Jan 31 '12 03:01

digitaljoel


People also ask

What does EntityManager merge do?

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).

What happens if EntityManager is not closed?

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.

How does merge work in JPA?

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.

What is the difference between Merge and persist in hibernate?

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.


2 Answers

Try the em.flush() also after the em.merge(). Sometimes the EntityManager just keeps the the changes for updating later.

like image 73
prajeesh kumar Avatar answered Oct 21 '22 08:10

prajeesh kumar


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();
}
like image 31
ethemsulan Avatar answered Oct 21 '22 07:10

ethemsulan