I want to share some domain objects between my client and server apps.
Starting with Hibernate 4, the Hibernate annotations were integrated in hibernate-core (used to be a separate jar), see http://in.relation.to/Bloggers/NoMoreHibernateannotationsModule
I would really prefer to stick with annotations (and not switch to xml based mapping).
At the same time I would really like to avoid bundling the hibernate core dependencies with the client (which will be transferred to the user via http through java web start, ideally on every startup).
Do I have to go back to Hibernate 3.x? Are these annotation dependencies needed to run the client or do I just need them to compile the client?
Thanks
You don't need to have the annotations of a class in the classpath to be able to load a class.
That said, a Hibernate entity contains collections and references to other entities. The collections are instances of Hibernate collections, and the references can be Hibernate proxies, if they're lazy-loaded. So if you serialize a Hibernate entity from the server to the client, the client will need the Hibernate jar(s) in its classpath to be able to load them.
If you want to avoid the Hibernate dependency at client-side, you should consider transferring DTOs to the client rather than Hibernate entities.
Related
Scenario:
I'm supporting an Enterprise application that runs in Wildfly10. The application (.war) uses J2EE technologies (EJBs, JPA, JAX-RS) and SpringBoot features (like, SpringMVC, SpringRest, SpringData, SpringRestData) ... Both stacks co-exists "happily" because they don't interact between them; however, they do share common classes, like utility or Entity Classes (the stacks map to the same database model). Why the application uses those stacks is out the scope of the question.
Currently, I'm trying to improve the performance of a #RestController that pulls some data from the database using a JPA Spring Repository. I found that we're suffering the N + 1 queries problem when calling the #RestController. In past projects (where there were only J2EE technologies), I have used the #BatchSize hibernate annotation to mitigate this problem with total success.
But, in this project, Spring seems to be skipping such annotation. How do I know that? Because I turned on the hibernate SQL logging (hibernate.show_sql) and I can see the N + 1 queries is still happening ...
Key Points:
Here are some insights about the application that you must know before providing (or trying to guess) any answer:
The application has many sub-modules encapsulated as libraries inside WAR file (/WEB-INF/lib) ... Some of these libraries are the jars that encapsulate the entity classes; others are the jars that encapsulate the REST Services (that could be JAX-RS services or Spring Controllers).
The Spring configuration is done in the classes defined in the WAR artifact: in there, we have a class (that extends from SpringBootServletInitializer) annotated with #SpringBootApplication and another class (that extends from RepositoryRestConfigurerAdapter) annotated with #Configuration. Spring customization is done is such class.
The application works with multiple datasources, which are defined in the Wildly server. Spring DATA JPA must address any query pointing to the right datasource. To accomplish this requirement, the application (Spring) was configured like this:
#Bean(destroyMethod="")
#ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
// the following class extends from AbstractRoutingDataSource
// and resolve datasources using JNDI names (the wildfly mode!)
return new DataSourceRouter();
}
#Bean("entityManagerFactory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean lemfb;
lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setPersistenceUnitName("abcd-pu");
lemfb.setDataSource(dataSource());
return lemfb;
}
The last #Bean declaration favors the use of a persistence.xml file, which we do have in the route /WEB-INF/classes/META-INF/ (i.e. Spring does find this file!) ... In such file, we define our domain classes, so that Spring JPA can see such entities. Also, we can define special JPA properties like: hibernate.show_sql and hibernate.use_sql_comments without issues (this is how I detected the N + 1 queries problem in the first place) ...
What I have done so far?
I tried to add the #BatchSize annotation to the problematic collection. No luck!
I created a new JAX-RS Service whose purpose was to mimic the behavior of the #RestController. I confirmed that the #BatchSize annotation does work in the application's deployment, at least, in JAX-RS Services! (NOTE: the service uses it own persistence.xml) ...
Test details (Updated 2020/07/30): What I did here was to create a new JAX-RS Service and deployed it inside the WAR application, next to the #RestController that presents the problem (I mean, it is the same WAR and the same physical JVM). Both services pull from database the same entity (same class - same classloader), which has a lazy Collection annotated with #BatchSize! ... If I invoke both services, the JAX-RS honors the #BatchSize and pulls the collection using the expected strategy, the #RestController does not ... So, what it is happening here? The only thing different between the services is that each one has a different persistence.xml: the persistence.xml for the JAX-RS is picked by Wildfly directly, the other one is picked by Spring and delegated to Wildfly (I guess) ...
I tried to add the properties: hibernate.batch_fetch_style (=dynamic) and hibernate.default_batch_fetch_size (=10), to the persistence.xml read by Spring ... No luck. I debug the Spring startup process and I saw that such properties are passed to the Spring Engine, but Spring does not care about them. The weird thing here is that properties like: hibernate.show_sql, Spring does honor them ... For those who are asking: "What does these properties do?" Well, they are global equivalent to apply #BatchSize to any JPA lazy collection or proxy without declaring such annotation in any entity.
I setup a small SpringBoot Project using the same Spring version as enterprise application (which is 1.5.8.RELEASE, by the way) and both the annotation and properties approach worked as supposed to.
I've been stuck with this issue for two days, any help to fix this will be appreciated ... thanks!
There are 2-3 possible issues that I can think off.
For some reason, whatever you modify isnt picked up by wildfly - Wildfly classpath resolution is a separate Topic and some missing configuration can cause you a nightmare. This you can identify if you have access to debug the query, and in if you put a breakpoint in the constructor of your Entity class, you will get a chance to evaluate the entity configuration being used, somewhere in the execution conetxt.
BatchSize doesnt work on OneToOne, It only works on OneToMany relationships.
A typical way to define BatchSize is to do along with Lazy load as mentioned in the example here. If you are not using Lazy fetch, hibernate assumes that you are willing to make an eager load and makes another select query to fetch all the details.Please confirm you are using the same syntax as given in the example above.
New Addition:
Put Conditional Breakpoints in PropertyBinder#setLazy() function, and may be backtrace it and put relavent breakpoints in CollectionBinder and AnnotationBinder. then restart/redeploy the server and see what data you are getting for the relavent properties. That will give you fair idea where it is failing..
Why conditional breakpoint? Its because you will have thousands of properties and if you do not add condition to the breakpoint, you will take 1 hour to reach your actual breakpoint
What should be the condition - If its property binder, the condition shoud be like `this.name == . For other classes also you can use the same approach.
Sorry for too detailed description on conditional breakpoints, you might find it redundent.
Looks like the only way to debug your problem is to debug hibernate framework from server startup, then only we will be able to find out the rootcause
I'm working on a web application that needs some dependencies, one of those being
<dependency>
<groupId>be.cocof.ddp</groupId>
<artifactId>ddp-model</artifactId>
<version>1.2</version>
</dependency>
(this dependency is developed by a 3rd-party which I can have contact with)
In this particular dependency, there are entities. In the pom.xml file, if I add the dependency like I typed above, I have an error in the deployment in wildfly : basically I use those entities but I don't have the mapped tables in my local database. Except that I only use those entities because I need them to build a request to send through a REST service developed by the same developer of the dependency. I don't need to persist all the entities information in my database.
So, how I can use this dependency without having to create tables in my database ?
I think, you cannot deploy your application without creation of the tables related to the entities. If using hibernate, you can set the property "hbm.ddl.auto-create" to "true" . This will automatically create the tables related to the entities while starting the server.
I have two projects (app.ui & models). models is a dependency in app.ui. There we have all the entity classes. app.ui is an errai project that uses JPA to save objects in the browser's storage.
So, I'm following the documentation in terms of annotations and stuff. If I create a class in app.ui and try to save it locally, it works. But if I have the same class with the exact same annotations in models.jar
java.lang.IllegalArgumentException: com.magick.models.shared.Strategy is not a known entity type
org.jboss.errai.jpa.client.local.ErraiMetamodel.entity(ErraiMetamodel.java:92)
org.jboss.errai.jpa.client.local.ErraiMetamodel.entity(ErraiMetamodel.java:108)
org.jboss.errai.jpa.client.local.ErraiMetamodel.entity(ErraiMetamodel.java:113)
org.jboss.errai.jpa.client.local.ErraiEntityManager.changeEntityState(ErraiEntityManager.java:180)
org.jboss.errai.jpa.client.local.ErraiEntityManager.persist(ErraiEntityManager.java:469)
app.client.local.strategy.StrategyProducer.saveStrategyLocally(StrategyProducer.java:83)
app.client.local.strategy.StrategyProducer.saveStrategiesInBrowser(StrategyProducer.java:78)
app.client.local.strategy.StrategyProducer.access$3(StrategyProducer.java:76)
app.client.local.strategy.StrategyProducer$1$1.callback(StrategyProducer.java:68)
app.client.local.strategy.StrategyProducer$1$1.callback(StrategyProducer.java:1)
Any ideas?
Thanks in advance.
In order to make this work, the model that needs to be persisted on browser's local storage it should be annotated with #Entity and should reside in the client-side package as errai ignores persistence.xml for errai-jpa-client and scans it in the client side package.
That is, in the package that GWT compiles.
Also try to use the browser plugin to check for the contents of local storage.
Hope this helps.
I'm creating a Java EE application that's using JPA for data access. Initially I used EclipseLink, but the bundled Geronimo Javamail implementation that it depends on via Moxy was giving me some odd issues and I couldn't force it to use Sun Javamail, so I've switched to Hiberate as the JPA provider.
EclipseLink was ignoring the lazy/eager annotations, it was eagerly loading everything. Hibernate pays attention to those annotations, and so dependant objects aren't loaded. That means if I load say a person, with a lazy loading of the persons parents, if I access the parents in the view it's not lazy loaded, I get an exception that says the database session's closed.
I understand there are two ways to get around this:
- Open Session in View pattern/antipattern (which isn't great from a layered point of view, and can have the N+1 database calls problem, but is easy)
- Have service methods that load all the data the view needs (which makes the service layer messy with lots of duplicate methods to get varying amounts of data)
For reference my layers are View -> Controller -> Service -> Entity Object -> JPA. I don't have a dto as it's a small app and I don't like the DTO anti-pattern.
Thinking about the Open Session in View pattern, the problem is the OpenSessionInViewInterceptor and OpenSessionInViewFilter are both Hibernate specific, and both require you to declare a hiberate session on your Spring configuration files. I prefer to stay with pure JPA, configured with a persistence.xml file.
What are my options here? Can I just change my Spring configuration to load Hibernate explicitly, but then use pure JPA inside my application? Is there a pure JPA way achieve the same result, lazy loading from the view?
it sounds odd the EclipseLink ignores standard JPA annotations.
the javamail implementation should not be in any way related to JPA
there is OpenEntityManagerInViewX (filter/interceptor) which handle the same scenario for JPA
you can easily go without this pattern if you declare and use your collections wisely.
I have a Java EE 6 web application that offers it's data via a JAX-RS REST web service.
The entities are annotated with the JPA annotations as well as with the javax.xml.bind JAX annotations.
My aim is to assemble a client-jar out of my web-app project that contains the JAX-RS annotated "DTO" classes to be used for JAX unmarshalling in clients of my web-app.
Putting the raw entities in the client jar is not an option because of the JPA annotations, which would lead to bogus dependencies for the client.
Is there a way for doing this without writing the JAX-RS classes twice, for the web-app and the clients?
I thought of annotation processing and killing all JPA annotations in the entities, that's quite techy, but not very handy.
My second idea is to extract an interface of the needed getters/setters of the entities. The question here is how to handle the JAX annotations that are placed at the class members and at the getters.
Both ways seem to work somehow. But is there a general purpose solution for that task?
Hint: yes, i'm aware of the way to expose the JPA-Entities directly via rest and its coupling drawbacks to evolution etc =)
You could supply the JPA metadata via XML (http://java.sun.com/xml/ns/persistence/orm_2_0.xsd) instead of annotations. This would give you the mapping without the class path dependency.
http://java.dzone.com/articles/persisting-entity-classes