views:

85

answers:

4

My problem is the same as described in [1] or [2]. I need to manually set a by default auto-generated value (why? importing old data). As described in [1] using Hibernate's entity = em.merge(entity) will do the trick.

Unfortunately for me it does not. I neither get an error nor any other warning. The entity is just not going to appear in the database. I'm using Spring and Hibernate EntityManager 3.5.3-Final.

Any ideas?

A: 

You need a running transaction.

In case your transaction are manually-managed:

entityManager.getTransaction().begin();

(of course don't forget to commit)

If you are using declarative transactions, use the appropriate declaration (via annotations, most likely)

Also, set the hibernate logging level to debug (log4j.logger.org.hibernate=debug) in your log4j.properties in order to trace what is happening in more details.

Bozho
Spring is doing that for me using @Transactional.
Jan
Then make sure the transaction is really started.
Bozho
@downvoter - while this might not be the issue with _this_ situation, it is often the problem when data is not inserted. So please explain your downvote.
Bozho
I think the point was good, even thought it was not the problem.
Jan
A: 

According to the Selectively disable generation of a new ID thread on the Hibernate forums, merge() might not be the solution (at least not alone) and you might have to use a custom generator (that's the second link you posted).

I didn't test this myself so I can't confirm but I recommend reading the thread of the Hibernate's forums.

Pascal Thivent
Thank you. Tried it and it works (as described by Kevin).
Jan
+1  A: 

it works on my project with the following code:

@XmlAttribute
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
                  strategy="....UseIdOrGenerate"
)
@Column(name = "ID", nullable = false)
private Integer id;

and

import org.hibernate.id.IdentityGenerator;
...
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class.getName());

@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
    if (obj == null) throw new HibernateException(new NullPointerException()) ;

    if ((((EntityWithId) obj).getId()) == null) {
        Serializable id = super.generate(session, obj) ;
        return id;
    } else {
        return ((EntityWithId) obj).getId();

    }
}

where you basically define your own ID generator (based on the Identity strategy), and if the ID is not set, you delegate the generation to the default generator.

The main drawback is that it bounds you to Hibernate as JPA provider ... but it works perfectly with my MySQL project

Kevin
Nice thing. I played around with it and it works. Unfortunately I was not able to use a generic SeqenceGenerator, because my dialect (mssql) won't support it. Curiously the @SeqenceGenerator works fine. However, this was the solution.
Jan
@Jan, it's funny, it's exactly the opposite with MySQL, SequenceGenerator is not supported, but IdentityGenerator works fine, "vive la portabilité!" (~portability rocks!)
Kevin
+1  A: 

For anyone else looking to do this, above does work nicely. Just a recommendation to getting the identifier from the object rather than having inheritance for each Entity class (Just for the Id), you could do something like:

import org.hibernate.id.IdentityGenerator;

public class UseIdOrGenerate extends IdentityGenerator {

    private static final Logger log = Logger.getLogger(UseIdOrGenerate.class
            .getName());

    @Override
    public Serializable generate(SessionImplementor session, Object object)
            throws HibernateException {
        if (object == null)
            throw new HibernateException(new NullPointerException());

        for (Field field : object.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)
                    && field.isAnnotationPresent(GeneratedValue.class)) {
                boolean isAccessible = field.isAccessible();
                try {
                    field.setAccessible(true);
                    Object obj = field.get(object);
                    field.setAccessible(isAccessible);
                    if (obj != null) {
                        if (Integer.class.isAssignableFrom(obj.getClass())) {
                            if (((Integer) obj) > 0) {
                                return (Serializable) obj;
                            }
                        }
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return super.generate(session, object);
    }
}
Jonathan