jeudi 26 septembre 2013

Hibernate and negative primarykeys on Oracle

I encounter today a common problem with Hibernate generating negative primary keys on Oracle.

If you are facing the problem described in the title of this post, you may jump directly to the end / the Solution.

Or, you may read the full story ;)

On JBoss 7.1.1 + Oracle, I declare entities like

@Id
@Column(name = "pk", insertable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "seq") 
@SequenceGenerator(name = "seq", sequenceName = "participant_pk_seq") 

Then, I added parameter allocationSize = 1 to @SequenceGenerator, because most of posts of smart people on the Web tells you to do it, but don't explain really why. And that this also mentioned on that confusing issue / still open on Hibernate bugtracker : :

https://hibernate.atlassian.net/browse/HHH-6486

So I did it, without understanding and it worked.
... Except from a oneToMany entity leaf, that was still set to
allocationSize = 12

And that was the begining of a very bad afternoon.

I noticed that generated primary keys, starts from 1 and then
-11
-10
-9
...
and then bang up to 1 with a ORA-00001.

So after a truncate, restart, it seems ok.
After having restarting my app server app : bing ! an other contraint exception is raised. Reloading my app, save my form oups seems OK ? sequence seems ok.
But after the next app server restart : bang again ! Another ORA-00001.
Cool down. This is only the fouth dimention.. great !

The Jboss RTFM wasn't not very helpful

http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-identifier

Confusing : they copy-pasted the recommendation of hibernate about persistence.xml property
"We recommend all new projects to use hibernate.id.new_generator_mappings=true"

But actually, Jboss 7 is doing it behind the scene / just for you, because they are very kind, and that you are very stupid and don't have to know about those very complicated settings.

the other RTFM adds some more confusion
https://docs.jboss.org/author/display/AS71/JPA+Reference+Guide#JPAReferenceGuide-Persistenceunitproperties
with the differences between GenerationType.AUTO and GenerationType.SEQUENCE
 
Then, I had 2 options.

I could either
1) hide the shit under the carpet with
allocationSize=1,
Meaning that a select ....nextval from dual will be done before each inserts.

and/or playing all the night long with the settings
hibernate.id.new_generator_mappings to false in my persistence.xml

OR
2) digging deeper into the Internet.

 I choosed option2.

I found this very intersting blog post talking about the risks of setting all allocationSize=1:

http://itdevworld.wordpress.com/2009/12/20/hibernate-sequencegenerator-with-allocationsize1-leads-to-huge-contention/

that post saved my day...

Actually allocationSize is a range of primarykey values reserved for Hibernate.
And the select ....nextval from dual is be done only after hibernate consumed this range of primarykeys.

So the same value MUST be the same on both allocationSize (Hibernate) AND sequence increment (DB)

When allocationSize=12, sequence creation on Oracle is

    create sequence participant_pk_seq
           MINVALUE 1
           MAXVALUE 999999999999999999999999999
           START WITH 1
           INCREMENT BY 12
           NOCACHE
           NOCYCLE;


On the Hibernate side, first call to persist my entities will allocate primary key range for instance
1...12

asking Oracle select participant_pk_seq.nextVal from dual
returns 13
and Hibernate fills its range until matching the end of its allocation / at 12.
Only after reaching primarykey=12 Hibernates asks again for  seq.nextVal for a new allocation.

So the only change to do in my application was... to change nothing in my application ;)
The change was on the DB side !

Post Scriptum.

@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerator {

    String name();

    String sequenceName() default "";

    String catalog() default "";

    String schema() default "";

    int initialValue() default 1;

    int allocationSize() default 50;
}

Conclusion:
If allocationSize is not declared on the entity side, as default allocation size is 50 ALL sequences  MUST declare INCREMENT BY 50 on the DB side.

This would be the recommended approach

1) By Default, declare all sequences with
      INCREMENT BY 50
2) Do not specify any allocationSize on entities
3) Except from entities that are expected to be inserted more than 50 at once, In that case, both allocationSize and increment size must be the same.








dimanche 28 juillet 2013

@EJB vs @Inject ?


Today, I dig into the internet about this question :

Should  I link my EJB Services to DAO / EJB persistance layer with
a) @EJB
OR
b) @Inject / à la CDI ?

THE source : Gavin King himself explains in that thread the detailed answer :

https://community.jboss.org/thread/179388#comment107817

It's amazing how Gavin is clear. Read the post fully : you'll have no doubt about the detailed answer :


@EJB is completely eliminated for local EJBs (except for backward compatibility). There are lots and lots of examples in the CDI spec and Weld documentation of using @Inject to inject local EJBs. And lots of times where I've explained that we strongly recommend the use of @Inject instead of @EJB to inject local EJBs. How could you possibly have missed that? Because you were too busy posting questions and brain-dead criticism instead of actually reading what is already out there?

Considering this excerpt, Gavin is touching the real problem here : I think I should improve the way I'm looking for answers ;)

On your side, I just hope that you didn't waste too much time on this question before finding this link. The noise on Stackoverflow  becomes anoying, hiding the real answers.






lundi 22 avril 2013

Setting up Activiti-CDI

If you follow the 'offical' user guide

http://www.activiti.org/userguide/#cdiintegration

You may be stuck on the following issue
org.activiti.engine.ActivitiException: Could not find an implementation of the org.activiti.cdi.spi.ProcessEngineLookup service returning a non-null processEngine. Giving up. at org.activiti.cdi.impl.ActivitiExtension.lookupProcessEngine(ActivitiExtension.java:106) at org.activiti.cdi.impl.ActivitiExtension.afterDeploymentValidation(ActivitiExtension.java:68)
And find many friends on the Internet with posts describing the same issue :

http://forums.activiti.org/content/activiti-cdi-exception
http://forums.activiti.org/content/issue-activiti-cdi-libraries

So we not just feeling alone; that is a good point ;)

Actually you may produce the Activiti Engine instance with a 'programatic' CDI OR a Spring activiti.cfg.xml. 
This is the complete JBoss setup on BitBucket :

https://bitbucket.org/meyerd/activiti-cdi-jbossas7.1.1-setup

The activiti user guide doesn't not explain clearly the two possibilities. Here there are :

(1) Default CDI producer starts up with org.activiti.cdi.impl.LocalProcessEngineLookup
   -> This is supposed to load the activiti.cfg.xml using Spring-context.
   -> You need to add /META-INF/beans.xml as usual for CDI.

(2) You may overload that default CDI producer writing your own (so that you get a chance to quit Spring some day).
    -> Provide a class that implements ProcessEngineLookup (see bitbucket resource).
    -> Define classname in /META-INF/services/org.activiti.cdi.spi.ProcessEngineLookup

Checking with the bitbucket sample code, (2) is OK, so what is wrong with (1) ?

The cause is contained within the first lines of the complete stacktrace : a NoClassDefFoundError (!)
Caused by: java.lang.NoClassDefFoundError: org/springframework/core/env/EnvironmentCapable


So actually, this is just a springframework version mismatch (!) The user guides still asks for adding spring-context3.0.3.RELEASE.
BUT currently, Activiti 5.12.1 defines spring 3.1.2...

With the correct version, activiti-CDI is stating as a charm !
These are the dependencies I used :

   

... Shall this post save your time ;)