Allan
Allan

Reputation: 2939

JPA ID Generation Strategy

I defined a generator for a JPA class:

<sequence-generator name="MY_SEQ" allocation-size="-1"
    sequence-name="MY_SEQ"
    initial-value="100000000" />

There are cases where I already have an ID for an entity but when I insert the Entity the Id gets generated using the generator.

Is it possible to define a generator that will only generate an Id when one does not exist?

I am using Hibernate as a JPA Provider.

Thank you

Upvotes: 2

Views: 1618

Answers (2)

Allan
Allan

Reputation: 2939

I couldn't find a way to do this in JPA so I used Hibernate EJB3 event listeners. I over rode the saveWithGeneratedId to use reflection to check the entity for an @Id annotation and then to check that field for a value. If it has a value then I call saveWithRequestedId instead. Other wise I let it generate the Id. This worked well because I can still use the sequence for Hibernate that is set up if I need an Id. The reflection might add overhead so I might change it a little. I was thinking of having a getId() or getPK() method in all entities so I don't have to search for which field is the @Id.

Before I used reflection I tried calling session.getIdentifier(entity) to check but I was getting TransientObjectException( "The instance was not associated with this session" ). I couldn;t figure out how to get the Entity into the session without saving it first so I gave up. Below is the listener code I wrote.

public class MergeListener extends org.hibernate.ejb.event.EJB3MergeEventListener
 {



    @Override
    protected Serializable saveWithGeneratedId(Object entity, String entityName, Object anything, EventSource source, boolean requiresImmediateIdAccess) {


        Integer id = null;


            Field[] declaredFields = entity.getClass().getDeclaredFields();

            for (Field field : declaredFields) {

                Id annotation = field.getAnnotation(javax.persistence.Id.class);

                if(annotation!=null) {


                    try {
                        Method method = entity.getClass().getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
                        Object invoke = method.invoke(entity);

                       id = (Integer)invoke;


                    } catch (Exception ex) {
                      //something failed (method not found..etc) , keep going anyway
                    }

                    break;

                }
            }



       if(id == null ||
                id == 0) {
        return super.saveWithGeneratedId(entity, entityName, anything, source, requiresImmediateIdAccess);
        } else {

            return super.saveWithRequestedId(entity, id, entityName, anything, source);
        }
    }
}

I then had to add the listener to my persistence.xml

 <property name="hibernate.ejb.event.merge" value="my.package.MergeListener"/>

Upvotes: 1

Luis Ramirez-Monterosa
Luis Ramirez-Monterosa

Reputation: 2242

it's not a good Idea, sequences are used for surrogate keys, are meaningless in the business sense but assures you, there won't be duplicates thus no error at inserting time.

Upvotes: 0

Related Questions