Hibernate Search behaves differently between DEV and PROD with same databse - java

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.

Related

How do I properly set up JPA with Intellj for a postgresql database and oracle persistence provider to get rid of runtime exceptions?

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.

JEE/JPA - How to use DAO/CRUD-Baseclass and Entities from other Projects (Eclipse)

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>

Exception regarding runtime optimization using openJPA MySQL

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.

Hibernate HQL returns stale data?

My Hibernate HQL query seems to be returning stale data.
I have a simple java class called Account, instances of which map onto a single database table with two varchar columns, username and surname.
If I run a HQL query such as:
List<?> accountList = session.createQuery("from Account where surname is null").list();
I get back a List of Account objects, as expected (some of the rows in the table indeed have null surname fields).
I then set the surname on the returned objects to some non-null value:
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
}
At this point, if I ran the HQL query again, I would expect to get back an empty List (as all surnames should be non-null),
but instead I get back the same objects as when I ran the query the first time. This is not what I expected.
Quoting from the Hibernate docs:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/
"there are absolutely no guarantees about when the Session executes the JDBC calls,
only the order in which they are executed. However, Hibernate does guarantee that the
Query.list(..) will never return stale or incorrect data."
This seems contrary to the behaviour of my code. Looking at the program output in Listing 4 below, the SQL Update statement happens after all the select statements, so the last select returns incorrect data.
Can anyone shed light on what is going on, or what I am doing wrong?
If I surround the setting of the surnames with a transaction, and perform a session.saveOrUpdate(account)
it all works, but I thought that this was not required.
I would like my code to only deal with the domain classes if possible, and be free
of persistence code as much as possible.
I am using Hibernate 4.1.8.Final, with Java 1.6
My full code listing is below:
Listing 1: Main.java:
package uk.ac.york.cserv.hibernatetest;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Main {
private static SessionFactory sf;
Session session;
public static void main(String[] args) {
Main main = new Main();
main.doExample();
}
public Main() {
sf = new Configuration()
.configure("hibernate-ora.cfg.xml")
.buildSessionFactory();
session = sf.openSession();
}
public void closeSession() {
session.flush();
session.close();
}
public List<?> getAccountList() {
return session.createQuery("from Account where surname is null").list();
}
public void printAccountList(List<?> accountList) {
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
System.out.println(accountIter.next());
}
}
public void log(String msg) {
System.out.println(msg);
}
public void doExample() {
log("Print all accounts with null surnames...");
printAccountList(getAccountList());
log("Adding surnames to accounts that have null surnames...");
//session.beginTransaction();
Iterator<?> accountIter = getAccountList().iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
//session.saveOrUpdate(account);
}
//session.getTransaction().commit();
log("Again print all accounts that have null surnames (should be none)...");
printAccountList(getAccountList());
closeSession();
}
}
Listing 2: Account.java:
package uk.ac.york.cserv.hibernatetest;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="ACCOUNTS")
public class Account {
#Id
#Column(name = "USERNAME", unique = true, nullable = false)
private String username;
#Column(name = "SURNAME")
private String surname;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
#Override
public String toString() {
return "Account [username=" + username + ", surname=" + surname + "]";
}
}
Listing 3: Hibernate-ora.cfg.xml:
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:#testhost:1521:test</property>
<property name="connection.username">testschema</property>
<property name="connection.password">testpassword</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Names of the annotated classes -->
<mapping class="uk.ac.york.cserv.hibernatetest.Account"/>
</session-factory>
</hibernate-configuration>
Listing 4: Output of the program:
Print all accounts with null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=null]
Adding surnames to accounts that have null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Adding surname of Jones to : user2
Again print all accounts that have null surnames (should be none)...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=Jones]
Hibernate: update ACCOUNTS set SURNAME=? where USERNAME=?
There is nothing strange about the Hibernate behavior you're describing
"At this point, if I ran the HQL query again, I would expect to get back an empty List (as all surnames should be non-null), but instead I get back the same objects as when I ran the query the first time. This is not what I expected."
At that point, when you run the HQL query again, you haven't done anything concerning the database so far. This is the reason why you're obtaining what you call 'stale' data but it's in fact the most current version of what is still unmodified in the table
If you issue the saveOrUpdate command and close the transaction the changes you have done in your Java class are persisted to database so that the new HQL query executions show the updated data
I think you're misunderstanding the way Hibernate works in this use case. Precisely because "Hibernate does guarantee that the Query.list(..) will never return stale or incorrect data." you see an updated version of the data coming from the database, from the database point of view your changes in your Java class are the 'stale' ones and are replaced by new "fresh" real data coming from the original still unmodified source

JPA/Hibernate Static Metamodel Attributes not Populated -- NullPointerException

I would like to use JPA2 Criteria API with metamodel objects, which seems to be pretty easy:
...
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
... albm.get(JPAAlbum_.theme) ... ;
but this Root.get always throws a NullPointerException. JPAAlbum_.theme was automatically generated by Hibernate and looks like
public static volatile SingularAttribute<JPAAlbum, JPATheme> theme;
but it's obviously never populated.
Am I missing a step in the initialization of the framework ?
EDIT: here is a snippet of how I use JPA and the metamodel when it's crashing:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<JPAAlbum> cq = cb.createQuery(JPAAlbum.class) ;
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
cq.where(cb.equal(albm.get(JPAAlbum_.theme).get(JPATheme_.id),
session.getTheme().getId())) ;
(JPAAlbum_ is a class, so I just import before) and the associated stacktrace:
Caused by: java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
at net.wazari.dao.jpa.WebAlbumsDAOBean.getRestrictionToAlbumsAllowed(WebAlbumsDAOBean.java:55)
EDIT 2:
In the JBoss EntityManager guide, I can see that
When the Hibernate EntityManagerFactory is being built, it will look for a canonical metamodel class for each of the managed typed is knows about and if it finds any it will inject the appropriate metamodel information into them, as outlined in [JPA 2 Specification, section 6.2.2, pg 200]
I could also verify with
for (ManagedType o : em.getMetamodel().getManagedTypes()) {
log.warn("___") ;
for (Object p : o.getAttributes()) {
log.warn(((Attribute)p).getName()) ;
}
}
that Hibernate is aware of my metamodel, the attribute names are written, however
log.warn("_+_"+JPAPhoto_.id+"_+_") ;
remains desperately empty ...
EDIT3: here is the JPAAlbum entity and its metamodel class.
What else can I tell about my configuration ...
I use Hibernat 3.5.6-Final (according to META-INF/MANIFEST.MF),
deploy on Glassfish 3.0.1
from Netbeans 6.9.1;
and the application relies on EJB 3.1,
I hope it will help !
EDIT 4:
unfortunately, the JUnit test leads to the same exception:
java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
at net.wazari.dao.test.TestMetaModel.foo(TestMetaModel.java:55)
A much simpler project is available here/tarball. It only contains my entities and their metamodel, plus a JUnit test (foo crashes with metamodel, bar is okay with the usual Query.
EDIT 5:
You should be able to reproduce the problem by downloading the tarball, building the project:
ant compile
or
ant dist
and start the JUnit test net.wazari.dao.test.TestMetaModel
CLASSPATH=`sh runTest.sh` java org.junit.runner.JUnitCore net.wazari.dao.test.TestMetaModel
(edit runTest.sh to point CLASSPATH to the right location of your JUnit4-5 jar)
All the hibernate dependencies I use should be included in the archive.
I had the same issue and it was fixed by putting the Model and Model_ class into the same package.
I had a Java EE 6 application using EclipseLink on GlassFish with some #StaticMetamodel classes created and everything was working fine. When I switched to Hibernate 4 on JBoss 7, I started getting these NPEs too. I started investigating and I found this page:
http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/metamodel.html
It quotes the JPA 2 specification, section 6.2.1.1 which defines how the static metamodel classes should be built. For example, I found out by reading the spec that "the option of different packages will be provided in a future release of this specification". I had the metamodel classes in different packages and it worked fine on EclipseLink, but it's an extra feature, as the current standard indicates the following:
Metamodel classes should be in the same package as the entity classes they describe;
They should have the same name as the entity classes they describe, followed by an underscore (e.g. Product is the entity, Product_ is the metamodel class);
If an entity inherits from another entity or from a mapped superclass, its metamodel class should inherit from the metamodel class that describes its immediate superclass (e.g. if SpecialProduct extends Product, which extends PersistentObject, then SpecialProduct_ should extend Product_ which should extend PersistentObject_).
Once I followed all the rules in the spec (the above is just a summary, please refer to section 6.2.1.1 of the spec for the complete version), I stopped getting the exceptions.
By the way, you can download the specification here: http://jcp.org/en/jsr/detail?id=317 (click on "Download page" for the final release, choose to download the specification for evaluation, accept the agreement and download the file "SR-000317 2.0 Specification" - persistence-2_0-final-spec.pdf).
I can't reproduce the issue. I used some of your entities (simplified versions of JPAAlbum, JPATheme and JPATagTheme, without any interfaces), generated the metamodel classes and the following rudimentary test method (running inside a transaction) just passes:
#Test
public void foo() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<JPAAlbum> query = builder.createQuery(JPAAlbum.class);
Root<JPAAlbum> album = query.from(JPAAlbum.class);
Assert.assertNotNull(album.get(JPAAlbum_.theme)); // no problem here
query.where(builder.equal(album.get(JPAAlbum_.theme).get(JPATheme_.id), 1L));
List<JPAAlbum> results = em.createQuery(query).getResultList();
}
FWIW, here is the generated SQL:
select
jpaalbum0_.ID as ID32_,
jpaalbum0_.AlbumDate as AlbumDate32_,
jpaalbum0_.Description as Descript3_32_,
jpaalbum0_.Nom as Nom32_,
jpaalbum0_.Picture as Picture32_,
jpaalbum0_.Theme as Theme32_
from
Album jpaalbum0_
where
jpaalbum0_.Theme=1
Tested with Hibernate EntityManager 3.5.6-Final, Hibernate JPAModelGen 1.1.0.Final, outside any container.
My suggestion would be to first try to reproduce (if reproducible) the problem in a JUnit test context.
PS: As a side note, I wouldn't store generated classes in the VCS.
Update: Here is a persistence.xml that you can use in a testing context:
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="2.0">
<persistence-unit name="MyPu" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.stackoverflow.q3854687.JPAAlbum</class>
<class>com.stackoverflow.q3854687.JPATheme</class>
<class>com.stackoverflow.q3854687.JPATagTheme</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<!-- Common properties -->
<property name="javax.persistence.jdbc.driver" value="${jdbc.driver}" />
<property name="javax.persistence.jdbc.url" value="${jdbc.url}" />
<property name="javax.persistence.jdbc.user" value="${jdbc.user}" />
<property name="javax.persistence.jdbc.password" value="${jdbc.password}" />
<!-- Hibernate specific properties -->
<property name="hibernate.dialect" value="${jdbc.dialect}" />
<!--
<property name="hibernate.show_sql" value="true"/>
-->
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
I offer an alternative solution if putting the Model and Model_ in the same package does not work. You need to add one init() method to your class that builds the SessionFactory or EntityManager:
public class HibernateSessionFactory {
private static SessionFactory factory;
static {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getFactory() {
return factory;
}
public static void init(){} //does nothing but elimating the NULLPOINTEREXCEPTION
}
So when you run your application from main method or a unit test you need to call HibernateSessionFactory.init(); first. Then the NullPointerException magically disappears and the application works.
This strange behaviour seems to happen when you pass a SingularAttribute around via method parameter.
Credit goes to #Can ÜNSAL who figured it all out in this question: Hibernate/JPA - NullPointerException when accessing SingularAttribute parameter
2019-04-24
The usual issue for unpopulated metamodel class attributes, is when the metamodel classes are in a different package than the corresponding managed classes.
The latest, JPA 2.2 specification still requires to have your metamodel classes in the same package as your corresponding managed classes.
Reference: Page 238, §6.2.1.1 Canonical Metamodel
FYI, I encountered a case where Hibernate creates a metamodel attribute but never initializes it, leading to a NullPointerException's when trying to use it.
public class Upper {
public String getLabel() { return this.label; }
public void setLabel(String label) { this.label = label; }
}
public class Lower extends Upper {
#Override
public String getLabel() { return super.getLabel(); }
}
Hibernate generate a label attribute declaration in both classes:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Upper.class)
public abstract class Upper_ {
public static volatile SingularAttribute<Upper, String> label;
}
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Lower.class)
public abstract class Lower_ {
public static volatile SingularAttribute<Lower, String> label;
}
...and it will initialize Upper_.label but leave Lower_.label equal to null.
Boom.
The class and the metaModel should be in the same package, i.e.
Folder entities:
Eje
Eje_
Element
Element_
I attached one example of the metamodel code
import javax.annotation.Generated;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
import java.util.Date;
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Eje.class)
public abstract class Eje_ {
public static volatile SingularAttribute<Eje, Integer> id;
public static volatile SingularAttribute<Eje, String> name;
public static volatile SingularAttribute<Eje, Integer> users;
public static volatile SingularAttribute<Eje, Date> createdAt;
public static volatile SingularAttribute<Eje, Date> updatedAt;
public static volatile SetAttribute<Eje, FactorCritico> factorCriticos;
}
If nothing of above resolve this NPE issue, you also can check whether you are using List in your Entities relationships instead of Set.
I found out that using List's it is needed to declare ListAttribute instead of SetAttribute in the metamodel, otherwise, it provoke a NullPointerException and if you don't see the whole stack trace you will not notice that the metamodel was not initialized by your JPA specification.
Debbie's answer got me half of the way there.
There is another "name matching" voodoo gotcha.
Short version:
The "names" have to match between Model and the MetaModel for the "properties".
Longer version:
I was using non-easy-peezy names.
First the entities:
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.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
#Entity
#Table(name = "DepartmentTable")
public class DepartmentJpaEntity implements Serializable {
#Id
#Column(name = "DepartmentKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long departmentKey;
#Column(name = "DepartmentName", unique = true)
private String departmentName;
#Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime createOffsetDateTime;
#OneToMany(
mappedBy = "parentDepartmentJpaEntity",
cascade = CascadeType.PERSIST,
orphanRemoval = true,
fetch = FetchType.LAZY /* Lazy or Eager here */
)
private Set<EmployeeJpaEntity> employeeJpaEntities = new LinkedHashSet<>();
}
and
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 java.io.Serializable;
import java.time.OffsetDateTime;
#Entity
#Table(name = "EmployeeTable")
public class EmployeeJpaEntity implements Serializable {
#Id
#Column(name = "EmployeeKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long employeeKey;
#Column(name = "Ssn")
private String ssn;
#Column(name = "LastName")
private String lastName;
#Column(name = "FirstName")
private String firstName;
#Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime createOffsetDateTime;
//region Navigation
#ManyToOne(fetch = FetchType.LAZY, targetEntity = DepartmentJpaEntity.class, cascade = CascadeType.PERSIST )
#JoinColumn(name = "DepartmentForeignKey")
private DepartmentJpaEntity parentDepartmentJpaEntity;
//endregion
}
Note, my somewhat not default names. pay attention to the OnetoMany and ManyToOne object names.
Now, my meta-models:
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)
public class DepartmentJpaEntity_ {
public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employees; /* DOES NOT WORK :( */
}
but this (below) will work, because I'm magic voodoo'ing the same names:
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)
public class DepartmentJpaEntity_ {
public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employeeJpaEntities;
}
and the other one:
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)
public class EmployeeJpaEntity_ {
public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartment; /*does NOT work...its null at run time...no voodoo name matching */
}
But this does work:
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)
public class EmployeeJpaEntity_ {
public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartmentJpaEntity;
}
One of the reasons I purposely use non "standard" names for things is to flush out these voodoo setup issues at the beginning......in order to try and fail early...vs a failLater (hopefully in QA, not in production) issue(s) because someone renamed a property which seems benign at the time.

Categories

Resources