user2709715
user2709715

Reputation: 57

JPA merge() on detached entity makes insert when it exists

i use JPA with hibernate provider. heres my part code:

@Entity
@IdClass(xxxPK.class)
@NamedQueries({
...
})
@Inheritance(strategy = InheritanceType.JOINED)
public class xxx extends AbstractEntity {

public static final String GET_TRADES = "getInstruments";
public static final String GET_STRUCTURE = "getStructure";
public static final String GET_ALL = "getAllTrades";

@Column(nullable = false)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "xxx")
@SequenceGenerator(name = "xxxx", sequenceName = "xxxxS", allocationSize = 1)
private Long systemId;

@Id
@Column(nullable = false)
private Integer version;
 //////

And an descendant:

@Entity
@PrimaryKeyJoinColumns(
    {
            @PrimaryKeyJoinColumn(name = "systemId"),
            @PrimaryKeyJoinColumn(name = "version")
    }
)
public class YYY extends xxx {
 //////

I create an YYY instance and persiti is as i expect. After that i have per one row in each table in db:

fx = Persistence.invoke(new Persistence.Invoke<YYY>() {
            @Override
            public YYY run(EntityManager em) {
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new FileReader(new ClassPathResource("json/XXX_from.json").getFile()));
                } catch (IOException ex) {
                    System.out.print("File is not found");
                }
                YYYfx = (YYY) EntityConverter.getConverter(YYY.class).fromJson(reader);
                em.persist(fx);
                return fx;
            }
        });

So, after that fx is detached and as SPEC. sais i can make it persistent in another session by calling merge(), so call it but:

Persistence.invoke(new Persistence.Invoke<Object>() {
    @Override
    public Object run(EntityManager em) {
        fx.setZZZ("A");
        em.merge(fx);
        return null;
    }
});

After that i have an another row in XXX and YYY tables with a new primary. It looks like provider takes fx as TRANSIENT not as DETACHED then it sais ok - it is a new object and i would insert it. I dont know what to do i ve lost 4 hours on such stupid case. Please help!

public class Persistence {

    private static EntityManagerFactory emf;

    public static EntityManagerFactory getEmf() {
        return emf;
    }

    public void setEmf(EntityManagerFactory emf) {
        Persistence.emf = emf;
    }

    public static <T> T invoke(Invoke<T> inv) {
        T result = null;
        EntityManager em = getEmf().createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
            try {
            result = inv.run(em);
            transaction.commit();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            em.close();
        }
        return result;
    }

    public interface Invoke<T> {
        public T run(EntityManager em);
    }

}

Upvotes: 1

Views: 1440

Answers (1)

Alan Hay
Alan Hay

Reputation: 23226

See API:

https://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#merge(T)

Returns: the managed instance that the state was merged to

So from your first call:

return em.persist(fx);

http://www.objectdb.com/java/jpa/persistence/detach

Detached objects can be attached to any EntityManager by using the merge method:

The content of the specified detached entity object is copied into an existing managed entity object with the same identity (i.e. same type and primary key). If the EntityManager does not manage such an entity object yet a new managed entity object is constructed. The detached object itself, however, remains unchanged and detached.

Upvotes: 2

Related Questions