There must be a bunch of questions regarding this, and I have read a few, but the answer still eludes me. I am new to JPA and I am just trying to test a simple application to see if I can configure the thing properly. It is a stand alone application meaning it will not be run with a web server or anything. The entity class looks like:
#Entity
public class Person{
#Id
private String userID = null;
#Transient
private UserState userState = null;
private String email = null;
private String name = null;
public Person(){
userID = null;
email = null;
name = null;
userState = null;
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UserState getUserState() {
return userState;
}
public void setUserState(UserState userState) {
this.userState = userState;
}
}
The main:
public class PersistenceTest {
public static void main(String[] args) {
System.out.println("creating person");
Person p = new Person();
p.setUserID("GregR");
p.setEmail("Gregory#company.de");
p.setUserState(UserState.ACTIVE);
System.out.println("done creating person GregR");
EntityManagerFactory factory = Persistence.createEntityManagerFactory(PersonService.PERSISTENCE_UNIT_NAME);
System.out.println("factory initialized");
EntityManager manager = factory.createEntityManager();
System.out.println("EntityManager initialized");
PersonService service = new PersonService(manager);
System.out.println("service initialized");
System.out.println("Beginning transaction");
manager.getTransaction().begin();
System.out.println("Transaction begun");
System.out.println("attempting to persist person");
service.persistEntity(p);
manager.getTransaction().commit();
System.out.println("person persisted");
System.out.println("beginning cleanup");
manager.close();
factory.close();
System.out.println("Cleanup has completed");
}
}
The config:
<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="PersonService" transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>de.hol.persistable.entities.Person</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/ConnectionWikisDB"/>
<property name="javax.persistence.jdbc.user" value="GregR"/>
<property name="javax.persistence.jdbc.password" value="myPassword"/>
</properties>
</persistence-unit>
</persistence>
The console printout:
creating person
done creating person GReeder
factory initialized
47 PersonService INFO [main] openjpa.Runtime - Starting OpenJPA 2.3.0
110 PersonService INFO [main] openjpa.jdbc.JDBC - Using dictionary class "org.apache.openjpa.jdbc.sql.MySQLDictionary".
306 PersonService INFO [main] openjpa.jdbc.JDBC - Connected to MySQL version 5.5 using JDBC driver MySQL-AB JDBC Driver version mysql-connector-java-5.0.8 ( Revision: ${svn.Revision} ).
Exception in thread "main" <openjpa-2.3.0-r422266:1540826 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: This configuration disallows runtime optimization, but the following listed types were not enhanced at build time or at class load time with a javaagent: "
de.hol.persistable.entities.Person".
at org.apache.openjpa.enhance.ManagedClassSubclasser.prepareUnenhancedClasses(ManagedClassSubclasser.java:115)
at org.apache.openjpa.kernel.AbstractBrokerFactory.loadPersistentTypes(AbstractBrokerFactory.java:312)
at org.apache.openjpa.kernel.AbstractBrokerFactory.initializeBroker(AbstractBrokerFactory.java:236)
at org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:212)
at org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:155)
at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:226)
at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:153)
at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:59)
at de.hol.persistable.PersistenceTest.main(PersistenceTest.java:24)
My Questions
1. I guess the main question is, what am I doing wrong. I am very new to this and am trying to just get this stand alone application to work so that I can expand it for real world use.
2. Am I missing some other configuration other than the persistence.xml file?
3. What is the simplest way of getting around this error for a stand-alone app?
Many thanks in advance!
I see you have main class, therefore I assume you are using it in Java SE environment. The easiest way to make it work is to define -javaagent in command line, like this:
java -jar myJAR.jar -javaagent:openjpa-all-2.3.0.jar
It is also possible from Eclipse: Run->Run Configurations->find your application in "Java Applications"->Arguments->VM arguments->add
-javaagent:/full/path/to/openjpa-all-2.3.0.jar
By inspecting [ManagedClassSubclasser]1 code you can see that this is more like a warning. If you want to silence this exception and log a warning instead by:
<property name="openjpa.RuntimeUnenhancedClasses" value="warn"/>
I would like to add my experience while faced same kind of problem. If you are usng the tomcat it doesn't implement the full spec for OpenJPA you need to provide the enhancements separately. For the enhancement follow the instructions available on this link http://openjpa.apache.org/enhancement-with-eclipse.html. And you will be good to go.
NOTE: i know you are not using any server, but in case you want to use tomcat then you need to follow the link.
Related
I am totally new to JPA and try to get at it with the tools provided from my University. So what I am trying to do is to set up a database carsdb with one table car and read the table from my main with a JPQL Query.
What I did so far:
I created a user carsdbuser with password carsdbpw, created a postgres database carsdb, which is owned by carsdbuser, added a table car and inserted a few columns.
I created a new Intellij project with JPA.
I then added the postgresql jdbc drivers (I think... the file was given to us by the university) postgresql-42.2.1.jar
as well as the oracle provider eclipslink.jar.
I then modified the persistence.xml to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="carsdb" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>Car</class>
<properties>
<property name="eclipselink.jdbc.url" value="jdbc:postgresql://localhost:5432/carsdb"/>
<property name="eclipselink.jdbc.driver" value="org.postgresql.Driver"/>
<property name="eclipselink.jdbc.user" value="carsdbuser"/>
<property name="eclipselink.jdbc.password" value="carsdbpw"/>
<property name="eclipselink.target-database" value="PostgreSQL"/>
<property name="eclipselink.logging.level" value="ALL"/>
</properties>
</persistence-unit>
</persistence>
I created the Class Car.java:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "car")
public class Car {
#Id
#GeneratedValue(generator = "incrementor")
private int Id;
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
private String Name;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
#Override
public String toString() {
return "Car " + Id + ": Name: " + Name;
}
}
as well as the Main:
import javax.persistence.*;
import java.util.List;
public class Main {
public static void main(String[] args){
var factory = Persistence.createEntityManagerFactory("carsdb");
EntityManager em = factory.createEntityManager();
Query query = em.createQuery("select a from Car a");
List<Car> list = query.getResultList();
for (Car c : list) {
System.out.println(c);
}
}
}
Given the tutorials I am following this looks good to me, however when I run the program I get the following Error Message:
Exception in thread "main" java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [select a from Car a].
[14, 17] The abstract schema type 'Car' is unknown.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1743)
at Main.main(Main.java:9)
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.1.v20171221-bd47e8f): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Problem compiling [select a from Car a].
[14, 17] The abstract schema type 'Car' is unknown.
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:155)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:347)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.populateQueryImp(HermesParser.java:278)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildQuery(HermesParser.java:163)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:140)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:116)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:102)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:86)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1741)
... 1 more
I tried to find some solutions with the help of some classmates and google, but did not find any solution that helped.
I added the Database in Intellij to make sure I got the right url. The connection test works properly and I also find my car table in the Intellij database view.
The following Question discusses a similar Issue:
Error on compiling query: The abstract schema type 'entity' is unknown
I do however have Car in the select statement which is the case sensitive name of the entity, so I cant see how it is related to my problem.
Multiple questions ahead.
This code works fine.
I'm working in Eclipse.
As you see from the whole setup, I'm using JTA.
Project layout
Local Project
- servlets
- _Index (using User)
- _Index2 (using Person)
- cruds
- UserCRUD
- PersonCRUD
- entities
- User
Remote/Other/Library Project
- entities
- Person
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="default" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/test</jta-data-source>
<class>tests.jee.simple.entities.User</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
<property name="javax.persistence.schema-generation.database.action" value="create" />
<property name="javax.persistence.schema-generation.scripts.action" value="create" />
<property name="javax.persistence.schema-generation.create-source" value="metadata-then-script" />
<property name="javax.persistence.schema-generation.drop-source" value="script-then-metadata" />
</properties>
</persistence-unit>
</persistence>
Servlet:
#WebServlet("/")
public class _Index extends HttpServlet {
private static final long serialVersionUID = -2039252988955365999L;
#EJB private UserCRUD mUserCRUD;
#Override protected void doGet(final HttpServletRequest pReq, final HttpServletResponse pResp) throws IOException {
final User u = new User();
u.setName("User #" + ((int) (Math.random() * 1000)));
mUserCRUD.persist(u);
pResp.getWriter().write("<pre>");
final List<User> all = mUserCRUD.getAll();
for (final User user : all) {
pResp.getWriter().write("\t" + user + "\r\n");
}
pResp.getWriter().write("</pre>");
}
}
Entity:
#Entity
public class User {
#Id #GeneratedValue private long id;
private String name;
public String getName() {
return name;
}
public void setName(final String pName) {
name = pName;
}
#Override public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
CRUD/DAO/EJB:
#Stateless
public class UserCRUD {
#PersistenceContext private EntityManager mEM;
public void persist(final User pUser) {
mEM.persist(pUser);
}
public List<User> getAll() {
final TypedQuery<User> query = mEM.createQuery("SELECT i FROM " + User.class.getSimpleName() + " i", User.class);
return query.getResultList();
}
}
Now I want to use Entities from other Eclipse projects.
Class Person in other Project
#Entity
#Table(name = "Company_Person")
public class Person {
#Id #GeneratedValue private long mId;
private String mName;
public Person() {}
public void setName(final String pName) {
mName = pName;
}
public String getName() {
return mName;
}
}
And CRUD in local project, right next to UserCRUD:
#Stateless
public class PersonCRUD {
#PersistenceContext private EntityManager mEM;
public void persist(final Person pUser) {
mEM.persist(pUser);
}
public List<Person> getAll() {
final TypedQuery<Person> query = mEM.createQuery("SELECT i FROM " + Person.class.getSimpleName() + " i", Person.class);
return query.getResultList();
}
}
Servlet #2 in local project
#WebServlet("/2")
public class _Index2 extends HttpServlet {
private static final long serialVersionUID = -2039252988955365999L;
#EJB private PersonCRUD mPersonCRUD;
#Override protected void doGet(final HttpServletRequest pReq, final HttpServletResponse pResp) throws IOException {
final Person u = new Person();
u.setName("User #" + ((int) (Math.random() * 1000)));
mPersonCRUD.persist(u);
pResp.getWriter().write("<pre>");
final List<Person> all = mPersonCRUD.getAll();
for (final Person user : all) {
pResp.getWriter().write("\t" + user + "\r\n");
}
pResp.getWriter().write("</pre>");
}
}
But as soon as I start referencing classes from other projects (including then in the build path), JPA goes weird:
First, it tells me on deployment, that JNDI lookup failed for the additional CRUD:
2018-03-23T17:43:41.835+0100|Severe: Exception while deploying the app [Test_JEE_Simple] : JNDI lookup failed for the resource: Name: [java:module/env/tests.jee.simple.servlets._Index2/mPersonCRUD], Lookup: [tests.jee.simple.crud.PersonCRUD#tests.jee.simple.crud.PersonCRUD], Type: [Session]
javax.naming.NamingException: Lookup failed for 'tests.jee.simple.crud.PersonCRUD#tests.jee.simple.crud.PersonCRUD' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NameNotFoundException: tests.jee.simple.crud.PersonCRUD#tests.jee.simple.crud.PersonCRUD not found]
JPA does not automatically find classes from other projects, throws me ClassNotFoundExceptions.
So I add the related project to Project->Properties->Deployment Assembly.
Accessing the users (/Test_JEE_Simple/ => _Index.class) works fine.
Accessing the users (/Test_JEE_Simple/2 => _Index2.class) does not work, throws
Caused by: java.lang.IllegalArgumentException: Object: User #706 (null/null) is not a known Entity type.
When I try adding Person.class to the persistence.xml entries, still same problem.
So here my questions:
Is it possible by only using JTA, or do I have to go RESOURCE_LOCAL at some point?
Is there a way to also have the PersonCRUD in the other project?
Usually I use a crud base class, and if need be, the specific classes - like UserCRUD and PersonCRUD - extend it. Works just fine. Don't wanna write loads of boilerplate code, but with that approach still can handle specific requests. So: Would it even be possible to have that generic CRUD-Class in another project?
Is there any way (annotations etc) to use the JTA's EntityManager Transaction? I saw a trick somewhere, but didn't test and save it, so it's lost to me...
UPDATE:
For some magic reason (and there seems to be a lot of magic) I can now use the Entities from other classes... Happened sometime after I added the other Project in local projects Deployment Assembly, undeployed, cleaned, closed, reopened and deployed the local project...
So the questions now remaining are 2-4, desired layout:
Local Project (referencing lib #1 and lib #2)
- servlets
- _Index (using User)
- _Index2 (using Person)
- cruds
- UserCRUD: CrudBase<User>
- entities
- User
Remote/Other/Library Project #1 (referencing lib #2)
- entities
- Person
- cruds
- PersonCRUD: CrudBase<Person>
Remote/Other/Library Project #2 (super-library)
- cruds
- CrudBase<T>
I have a Jax RS server which is correctly called when a POST request is sent with a Movie in XML format.
#Resource(name = "movie")
#Path("/movie")
public class MovieResource
{
#PersistenceContext(unitName = "movieDS")
private EntityManager em;
public MovieResource()
{
em = PersistenceProvider.createEntityManager();
}
#POST
#Path("/post")
#Consumes(
{
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML
})
public Response createMovie(Movie movie)
{
if (!em.contains(newMovie))
{
em.merge(newMovie);
}
String result = "Movie created : " + movie;
return Response.status(201).entity(movie).build();
}
}
debugging shows no errors whatsoever however nothing is persisted.
The datasource is JTA over EclipseLink, here is persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!-- The data source should be set up in Glassfish -->
<persistence-unit name="MovieManager" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/movieDS</jta-data-source>
<properties>
<property name="eclipselink.logging.level" value="ALL"/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>
The logs returned from EclipseLink show no error whatsoever while calling em.merge(), they mostly involve sequence creation:
FINEST: Execute query ValueReadQuery(name="SEQUENCE" sql="SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = #SEQ_NAME")
FINE: SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [1 parameter bound]
FINEST: local sequencing preallocation for SEQ_GEN: objects: 50 , first: 51, last: 100
INFO: RAR7115: Unable to set ClientInfo for connection
FINEST: Connection released to connection pool [default].
FINER: TX commitTransaction, status=STATUS_ACTIVE
FINER: TX Internally committing
FINEST: local sequencing preallocation is copied to preallocation after transaction commit
FINER: external transaction has committed internally
FINEST: assign sequence to the object (51 -> net.plarz.entities.LoginInformation#91229c)
Has anyone any idea what's missing? the Movie class is very simple and has no dependencies with other tables, I think its something really simple I'm missing.
EDIT :
If I add a flush() after merge() I get an error:
javax.persistence.TransactionRequiredException:
Exception Description: No externally managed transaction is currently active for this thread
You shouldn't call flush() but instead create an enterprise bean so:
#Stateless
public class MovieEJB
{
#PersistenceContext(unitName = "movieDS")
private EntityManager em;
#Override
public Movie create(Movie movie) throws Exception
{
em.persist(movie);
return movie;
}
#Override
public void delete(Movie movie)
{
em.remove(em.merge(movie));
}
#Override
public Movie update(Movie movie) throws Exception
{
return em.merge(movie);
}
}
then modify your MovieResource class so:
#ManagedBean(name = "restController")
#SessionScoped
#Resource(name = "movie")
#Path("/movie")
public class MovieResource
{
#EJB
private MovieEJB movieEJB;
public MovieResource()
{
}
public MovieEJBLocal getMovieEJB()
{
return movieEJB;
}
public void setMovieEJB(MovieEJBLocal movieEJB)
{
this.movieEJB = movieEJB;
}
#POST
#Path("/post")
#Consumes(
{
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML
})
public Response createMovie(Movie movie)
{
getMovieEJB().create(movie);
String result = "Movie created : " + movie;
return Response.status(201).entity(movie).build();
}
}
You are using a JTA persistence unit, but not starting a JTA transaction.
Either switch to RESOURCE_LOCAL and non-JTA DataSource, or use a JTA transaction such as using an EJB (see other answer).
I have one domain object that needs to be indexed by Hibernate Search. When I do a FullTextQuery on this object on my DEV machine, I get the expected results. I then deploy the app to a WAR and explode it to my PROD server (a VPS). When I perform the same "search" on my PROD machine, I don't get the expected results at all (it seems like some results are missing).
I've run LUKE to ensure that everything was properly indexed, and it appears that everything is where it should be... I'm new to Hibernate Search, so any help would be appreciated.
Here's my domain Object:
package com.chatter.domain;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.solr.analysis.LowerCaseFilterFactory;
import org.apache.solr.analysis.SnowballPorterFilterFactory;
import org.apache.solr.analysis.StandardTokenizerFactory;
import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.annotations.Parameter;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.TokenFilterDef;
import org.hibernate.search.annotations.TokenizerDef;
#Entity
#Table(name="faq")
#Indexed()
#AnalyzerDef(name = "customanalyzer",
tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = {
#Parameter(name = "language", value = "English")
})
})
public class CustomerFaq implements Comparable<CustomerFaq>
{
private Long id;
#IndexedEmbedded
private Customer customer;
#Field(index=Index.TOKENIZED, store=Store.NO)
private String question;
#Field(index=Index.TOKENIZED, store=Store.NO)
private String answer;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
#ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="customer_id")
public Customer getCustomer()
{
return customer;
}
public void setCustomer(Customer customer)
{
this.customer = customer;
}
#Column(name="question", length=1500)
public String getQuestion()
{
return question;
}
public void setQuestion(String question)
{
this.question = question;
}
#Column(name="answer", length=1500)
public String getAnswer()
{
return answer;
}
public void setAnswer(String answer)
{
this.answer = answer;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
CustomerFaq other = (CustomerFaq) obj;
if (id == null)
{
if (other.id != null) return false;
} else if (!id.equals(other.id)) return false;
return true;
}
#Override
public int compareTo(CustomerFaq o)
{
if (this.getCustomer().equals(o.getCustomer()))
{
return this.getId().compareTo(o.getId());
}
else
{
return this.getCustomer().getId().compareTo(o.getCustomer().getId());
}
}
}
Here's a snippet of my Customer domain object:
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Store;
import javax.persistence.Entity;
// ... other imports
#Entity
public class Customer
{
#Field(index=Index.TOKENIZED, store=Store.YES)
private Long id;
// ... other instance vars
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
And my persistence.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- value="create" to build a new database on each run; value="update" to modify an existing database; value="create-drop" means the same as "create" but also drops tables when Hibernate closes; value="validate" makes no changes to the database -->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
<property name="hibernate.connection.charSet" value="UTF-8"/>
<!-- Hibernate Search configuration -->
<property name="hibernate.search.default.directory_provider"
value="filesystem" />
<property name="hibernate.search.default.indexBase" value="C:/lucene/indexes" />
</properties>
</persistence-unit>
</persistence>
And finally, here's the query that's being used in a DAO:
public List<CustomerFaq> searchFaqs(String question, Customer customer)
{
FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(CustomerFaq.class).get();
org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword().onFields("question", "answer").matching(question).createQuery();
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, CustomerFaq.class);
List<CustomerFaq> matchingQuestionsList = fullTextQuery.list();
log.debug("Found " + matchingQuestionsList.size() + " matching questions");
List<CustomerFaq> list = new ArrayList<CustomerFaq>();
for (CustomerFaq customerFaq : matchingQuestionsList)
{
log.debug("Comparing " + customerFaq.getCustomer() + " to " + customer + " -> " + customerFaq.getCustomer().equals(customer));
log.debug("Does list already contain this customer FAQ? " + list.contains(customerFaq));
if (customerFaq.getCustomer().equals(customer) && !list.contains(customerFaq))
{
list.add(customerFaq);
}
}
log.debug("Returning " + list.size() + " matching questions based on customer: " + customer);
return list;
}
It looks like the actual location where my software was looking for the indexBase was incorrect.
When I looked through the logs, I noticed that it was referring to two different locations when loading the indexBase.
One location that Hibernate Search was loading this indexBase was from "C:/Program Files/Apache Software Foundation/Tomcat 6.0/tmp/indexes", then a little later on in the logs (during the startup phase) I saw that it was also loading from the place I had set it to in my persistence.xml file ("C:/lucene/indexes").
So realizing this, I just changed the location in my persistence.xml file to match the location that it was (for some reason) also looking. Once those two matched up, BINGO, everything worked!
Just blind shot, if possible make your DEV env point to the PROD DB to see if you get the results that you're expecting.
Only to discard and be 100% sure that you're in front of the real problem :)
I can see that in your persistence.xml configuration that you're under Mysql. Googling some sql concepts about same queries in different environments I found that exists a cached mysql resultset from same queries, but this cached maybe changes depending on new variables from the environment like charset. Also you can disable this feature from your Mysql server.
Cited from Hibernate In Action - Bauer, C. King, G. Page 55 (Section 2.4.3 Logging)
But, especially in the face of asynchronous behavior, debugging Hibernate can quickly
get you lost. You can use logging to get a view of Hibernate’s internals.
We’ve already mentioned the *hibernate.show_sql* configuration parameter,
which is usually the first port of call when troubleshooting. Sometimes the SQL
alone is insufficient; in that case, you must dig a little deeper.
Hibernate logs all interesting events using Apache commons-logging, a thin
abstraction layer that directs output to either Apache log4j (if you put log4j.jar
in your classpath) or JDK1.4 logging (if you’re running under JDK1.4 or above and
log4j isn’t present). We recommend log4j, since it’s more mature, more popular,
and under more active development.
To see any output from log4j, you’ll need a file named log4j.properties in your
classpath (right next to hibernate.properties or hibernate.cfg.xml). This example
directs all log messages to the console:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### root logger option ###
log4j.rootLogger=warn, stdout
### Hibernate logging options ###
log4j.logger.net.sf.hibernate=info
### log JDBC bind parameters ###
log4j.logger.net.sf.hibernate.type=info
### log PreparedStatement cache activity ###
log4j.logger.net.sf.hibernate.ps.PreparedStatementCache=info
With this configuration, you won’t see many log messages at runtime. Replacing
info with debug for the log4j.logger.net.sf.hibernate category will reveal the
inner workings of Hibernate. Make sure you don’t do this in a production environment—
writing the log will be much slower than the actual database access.
Finally, you have the hibernate.properties, hibernate.cfg.xml, and
log4j.properties configuration files.
For an answer scroll down to the end of this...
The basic problem is the same as asked multiple time. I have a simple program with two POJOs Event and User - where a user can have multiple events.
#Entity
#Table
public class Event {
private Long id;
private String name;
private User user;
#Column
#Id
#GeneratedValue
public Long getId() {return id;}
public void setId(Long id) { this.id = id; }
#Column
public String getName() {return name;}
public void setName(String name) {this.name = name;}
#ManyToOne
#JoinColumn(name="user_id")
public User getUser() {return user;}
public void setUser(User user) {this.user = user;}
}
The User:
#Entity
#Table
public class User {
private Long id;
private String name;
private List<Event> events;
#Column
#Id
#GeneratedValue
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
#Column
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#OneToMany(mappedBy="user", fetch=FetchType.LAZY)
public List<Event> getEvents() { return events; }
public void setEvents(List<Event> events) { this.events = events; }
}
Note: This is a sample project. I really want to use Lazy fetching here.
Now we need to configure spring and hibernate and have a simple basic-db.xml for loading:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" scope="thread">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" />
<property name="username" value="root" />
<property name="password" value="" />
<aop:scoped-proxy/>
</bean>
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope" />
</entry>
</map>
</property>
</bean>
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread">
<property name="dataSource" ref="myDataSource" />
<property name="annotatedClasses">
<list>
<value>data.model.User</value>
<value>data.model.Event</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
<aop:scoped-proxy/>
</bean>
<bean id="myUserDAO" class="data.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<bean id="myEventDAO" class="data.dao.impl.EventDaoImpl">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
</beans>
Note: I played around with the CustomScopeConfigurer and SimpleThreadScope, but that didnt change anything.
I have a simple dao-impl (only pasting the userDao - the EventDao is pretty much the same - except with out the "listWith" function:
public class UserDaoImpl implements UserDao{
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
#SuppressWarnings("unchecked")
#Override
public List listUser() {
return hibernateTemplate.find("from User");
}
#Override
public void saveUser(User user) {
hibernateTemplate.saveOrUpdate(user);
}
#Override
public List listUserWithEvent() {
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
return users;
}
}
I am getting the org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at the line with user.getEvents().size();
And last but not least here is the Test class I use:
public class HibernateTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");
UserDao udao = (UserDao) ac.getBean("myUserDAO");
EventDao edao = (EventDao) ac.getBean("myEventDAO");
System.out.println("New user...");
User user = new User();
user.setName("test");
Event event1 = new Event();
event1.setName("Birthday1");
event1.setUser(user);
Event event2 = new Event();
event2.setName("Birthday2");
event2.setUser(user);
udao.saveUser(user);
edao.saveEvent(event1);
edao.saveEvent(event2);
List users = udao.listUserWithEvent();
System.out.println("Events for users");
for (User u : users) {
System.out.println(u.getId() + ":" + u.getName() + " --");
for (Event e : u.getEvents())
{
System.out.println("\t" + e.getId() + ":" + e.getName());
}
}
((ConfigurableApplicationContext)ac).close();
}
}
and here is the Exception:
1621 [main] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
at HibernateTest.main(HibernateTest.java:44)
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
at HibernateTest.main(HibernateTest.java:44)
Things tried but did not work:
assign a threadScope and using beanfactory (I used "request" or "thread" - no difference noticed):
// scope stuff
Scope threadScope = new SimpleThreadScope();
ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
beanFactory.registerScope("request", threadScope);
ac.refresh();
...
Setting up a transaction by getting the session object from the deo:
...
Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction();
tx.begin();
users = udao.listUserWithEvent();
...
getting a transaction within the listUserWithEvent()
public List listUserWithEvent() {
SessionFactory sf = hibernateTemplate.getSessionFactory();
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
tx.begin();
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
tx.commit();
return users;
}
I am really out of ideas by now. Also, using the listUser or listEvent just work fine.
Step forward:
Thanks to Thierry I got one step further (I think). I created the MyTransaction class and do my whole work in there, getting everything from spring. The new main looks like this:
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");
// getting dao
UserDao udao = (UserDao) ac.getBean("myUserDAO");
EventDao edao = (EventDao) ac.getBean("myEventDAO");
// gettting transaction template
TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate");
MyTransaction mt = new MyTransaction(udao, edao);
transactionTemplate.execute(mt);
((ConfigurableApplicationContext)ac).close();
}
Unfortunately now there is a null-pointer Exception #: user.getEvents().size(); (in the daoImpl).
I know that it should not be null (neither from the output in the console nor from the db layout).
Here is the console output for more information (I did a check for user.getEvent() == null and printed "EVENT is NULL"):
New user...
Hibernate: insert into User (name) values (?)
Hibernate: insert into User (name) values (?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
List users:
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
1:User1
2:User2
List events:
Hibernate: select event0_.id as id1_, event0_.name as name1_, event0_.user_id as user3_1_ from Event event0_
1:Birthday1 for 1:User1
2:Birthday2 for 1:User1
3:Wedding for 2:User2
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
Events for users
1:User1 --
EVENT is NULL
2:User2 --
EVENT is NULL
You can get the sample project from http://www.gargan.org/code/hibernate-test1.tgz (it's an eclipse/maven project)
The solution (for console applications)
There are actually two solutions for this problem - depending on your environment:
For a console application you need a transaction template which captures the actutal db logic and takes care of the transaction:
public class UserGetTransaction implements TransactionCallback{
public List users;
protected ApplicationContext context;
public UserGetTransaction (ApplicationContext context) {
this.context = context;
}
#Override
public Boolean doInTransaction(TransactionStatus arg0) {
UserDao udao = (UserDao) ac.getBean("myUserDAO");
users = udao.listUserWithEvent();
return null;
}
}
You can use this by calling:
TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
UserGetTransaction mt = new UserGetTransaction(context);
transactionTemplate.execute(mt);
In order for this to work you need to define the template class for spring (ie. in your basic-db.xml):
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
Another (possible) solution
thanks andi
PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager");
DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);
transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
boolean success = false;
try {
new UserDataAccessCode().execute();
success = true;
} finally {
if (success) {
transactionManager.commit(status);
} else {
transactionManager.rollback(status);
}
}
The solution (for servlets)
Servlets are not that big of a problem. When you have a servlet you can simply start and bind a transaction at the beginning of your function and unbind it again at the end:
public void doGet(...) {
SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
// Your code....
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
I think you should not use the hibernate session transactional methods, but let spring do that.
Add this to your spring conf:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
and then I would modify your test method to use the spring transaction template:
public static void main(String[] args) {
// init here (getting dao and transaction template)
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// do your hibernate stuff in here : call save, list method, etc
}
}
}
as a side note, #OneToMany associations are lazy by default, so you don't need to annotate it lazy. (#*ToMany are LAZY by default, #*ToOne are EAGER by default)
EDIT: here is now what is happening from hibernate point of view:
open session (with transaction start)
save a user and keep it in the session (see the session cache as an entity hashmap where the key is the entity id)
save an event and keep it in the session
save another event and keep it in the session
... same with all the save operations ...
then load all users (the "from Users" query)
at that point hibernate see that it has already the object in its session, so discard the one it got from the request and return the one from the session.
your user in the session does not have its event collection initialized, so you get null.
...
Here are some points to enhance your code:
in your model, when collection ordering is not needed, use Set, not List for your collections (private Set events, not private List events)
in your model, type your collections, otherwise hibernate won't which entity to fetch (private Set<Event> events)
when you set one side of a bidirectional relation, and you wish to use the mappedBy side of the relation in the same transaction, set both sides. Hibernate will not do it for you before the next tx (when the session is a fresh view from the db state).
So to address the point above, either do the save in one transaction, and the loading in another one :
public static void main(String[] args) {
// init here (getting dao and transaction template)
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// save here
}
}
transactionTemplate.execute(new TransactionCallback() {
#Override
public Object doInTransaction(TransactionStatus status) {
// list here
}
}
}
or set both sides:
...
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(event1,event2));
...
(Also do not forget to address the code enhancement points above, Set not List, collection typing)
In case of Web application, it is also possible to declare a special Filter in web.xml, that will do session-per-request:
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
After that you can lazyload your data anytime during the request.
I got here looking for a hint regarding a similar problem. I tried the solution mentioned by Thierry and it didnt work. After that I tried these lines and it worked:
SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
Indeed what I'm doing is a batch process that must leverage Spring existings managers/services. After loading the context and doing some invocations I founded the famous issue "failed to lazily initialize a collection". Those 3 lines solved it for me.
The issue is that your dao is using one hibernate session but the lazy load of the user.getName (I assume that is where it throws) is happening outside that session -- either not in a session at all or in another session. Typically we open up a hibernate session before we make DAO calls and don't close it until we are done with all lazy loads. Web requests are usually wrapped in a big session so these problems do not happen.
Typically we have wrapped our dao and lazy calls in a SessionWrapper. Something like the following:
public class SessionWrapper {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public <T> T runLogic(Callable<T> logic) throws Exception {
Session session = null;
// if the session factory is already registered, don't do it again
if (TransactionSynchronizationManager.getResource(sessionFactory) == null) {
session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
try {
return logic.call();
} finally {
// if we didn't create the session don't unregister/release it
if (session != null) {
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.releaseSession(session, sessionFactory);
}
}
}
}
Obviously the SessionFactory the same SessionFactory that was injected into your dao.
In your case, you should wrap the entire listUserWithEvent body in this logic. Something like:
public List listUserWithEvent() {
return sessionWrapper.runLogic(new Callable<List>() {
public List call() {
List users = hibernateTemplate.find("from User");
for (User user : users) {
System.out.println("LIST : " + user.getName() + ":");
user.getEvents().size();
}
}
});
}
You will need to inject the SessionWrapper instance into your daos.
Interesting!
I had the same problem in a #Controller's #RequestMapping handler method.
The simple solution was to add a #Transactional annotation to the handler method so that the session is kept open for the whole duration of the method body execution
Easiest solution to implement:
Within the scope of the session[inside the API annotated with #Transactional], do the following:
if A had a List<B> which is lazily loaded, simply call an API which makes sure the List is loaded
What's that API ?
size(); API of the List class.
So all that's needed is:
Logger.log(a.getBList.size());
This simple call of logging the size makes sure it gets the whole list before calculating the size of the list. Now you will not get the exception !
What worked for us in JBoss was the solution #2 taken from this site at Java Code Geeks.
Web.xml:
<filter>
<filter-name>ConnectionFilter</filter-name>
<filter-class>web.ConnectionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ConnectionFilter</filter-name>
<url-pattern>/faces/*</url-pattern>
</filter-mapping>
ConnectionFilter:
import java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.transaction.UserTransaction;
public class ConnectionFilter implements Filter {
#Override
public void destroy() { }
#Resource
private UserTransaction utx;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
utx.begin();
chain.doFilter(request, response);
utx.commit();
} catch (Exception e) { }
}
#Override
public void init(FilterConfig arg0) throws ServletException { }
}
Maybe it would work with Spring too.