I am using EJB 3.0 and Hibernate 4 with PostgreSQL as my database server to create a multitenant system where each tenant will have separate but identical schema. I am still in the trial stage where I have 3 schemes public, company1, company2 all having a single table person. Now what i want to do is change the schema in runtime as per the user so that he can view the data of his/her company only.
Here is my sample code:
Entity Object:
package com.neebal.domain;
import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import javax.persistence.*;
import org.eclipse.persistence.annotations.Multitenant;
import org.eclipse.persistence.annotations.MultitenantType;
#Entity
//#Table(schema = "company1")
public class Person implements Serializable {
#Id
private Long id;
private String name;
private static final long serialVersionUID = 1L;
public Person() {
super();
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
The MultiTenantConnectionProvider class:
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.service.config.spi.ConfigurationService;
import org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider;
import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public class MultiTenantProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private static final long serialVersionUID = 4368575201221677384L;
private C3P0ConnectionProvider connectionProvider = null;
#Override
public boolean supportsAggressiveRelease() {
return false;
}
#Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
connectionProvider = new C3P0ConnectionProvider();
connectionProvider.injectServices(serviceRegistry);
connectionProvider.configure(lSettings);
}
#Override
public boolean isUnwrappableAs(Class clazz) {
return false;
}
#Override
public <T> T unwrap(Class<T> clazz) {
return null;
}
#Override
public Connection getAnyConnection() throws SQLException {
final Connection connection = connectionProvider.getConnection();
return connection;
}
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
#Override
public void releaseAnyConnection(Connection connection) throws SQLException {
try {
connection.createStatement().execute("SET SCHEMA 'public'");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [public]", e);
}
connectionProvider.closeConnection(connection);
}
#Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
releaseAnyConnection(connection);
}
}
The CurrentTenantIdentifierResolver class:
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
public class SchemaResolver implements CurrentTenantIdentifierResolver {
#Override
public String resolveCurrentTenantIdentifier() {
System.out.println("company1");
return "company1"; //TODO: Implement service to identify tenant like: userService.getCurrentlyAuthUser().getTenantId();
}
#Override
public boolean validateExistingCurrentSessions() {
return false;
}
}
The persistence.xml file:
<?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">
<persistence-unit name="testEJB">
<jta-data-source>jdbc/testpgsql</jta-data-source>
<properties>
<property name="javax.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" />
<property name="hibernate.connection.username" value="postgres" />
<property name="hibernate.connection.password" value="root" />
<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/test" />
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.multiTenancy" value="SCHEMA" />
<property name="hibernate.tenant_identifier_resolver" value="com.neebal.util.multitenancy.SchemaResolver" />
<property name="hibernate.multi_tenant_connection_provider"
value="com.neebal.util.multitenancy.MultiTenantProvider" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
And finally the DAO class:
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.neebal.domain.Person;
/**
* Session Bean implementation class PersonDAO
*/
#Stateless
public class PersonDAO implements PersonDAOLocal {
#PersistenceContext
EntityManager entityManager;
/**
* Default constructor.
*/
public PersonDAO() {
// TODO Auto-generated constructor stub
}
#Override
public void save(Person person) {
entityManager.persist(person);
}
#Override
public List<Person> getAll() {
Person person = entityManager.find(Person.class, 2L);
System.out.println(person.getName());
return null;
}
}
In this example I have hardcoded the schema as company1 but it still persists or retrieves the data from public schema. So where am I wrong in this example.
The question is already 1 year old, but I think the problem of using different schemas depending on some runtime condition is common one, so I'll answer anyway. If I understand you right and the set of tenants is small, then I think the easiest way to do what you're trying to achieve is to define a separate persistence units for each tenant in your persistence.xml
<persistence-unit name="public">
.. settings for first schema
</persistence-unit>
<persistence-unit name="company1">
.. settings for first schema
</persistence-unit>
<persistence-unit name="company2">
.. settings for first schema
</persistence-unit>
Then have for each one a separate entityManager:
#PersistenceContext(unitName = "public")
private EntityManager emPublic;
#PersistenceContext(unitName = "company1")
private EntityManager emComp1;
#PersistenceContext(unitName = "company2")
private EntityManager emComp1;
Now you can switch between entity managers, given the currently authorized user.
Depending on your exact infrastructure etc, there may be other approaches, too. For instance, if all your schemas are on the same server, then you could also try to pass the schema names directly to your queries.
This is pure JPA and thus portable and not depending on any persistence provider like hibernate nor on your DBMS.
Related
I'm working with a Maven project using Hibernate (hibernate-entitymanager), a MySQL connector (mysql-connector-java) and JUnit (junit-jupiter-engine), and I want to do a rollback database operation after a JUnit Test
Well, I've already tried with:
Put #Transactional annotation in the test class and test case
Create a new method savePerson(Person p, boolean isRollback) pass an extra parameter isRollback and put some if conditions.
Create a Service class PersonServiceImpl and put #Transactional annotation in the top level class.
Unfortunately, It didn't work.
I have an pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>entity-manager-07</artifactId>
<version>1.0</version>
<name>entity-manager-07</name>
<description>entity-manager-07</description>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.2.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.2.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.0-M1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.0-M1</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
An Person Entity
package com.example.entities;
import javax.persistence.*;
#Entity
#Table(name = "Person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "lastname")
private String lastname;
public Person() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Person [id=");
builder.append(id);
builder.append(", name=");
builder.append(name);
builder.append(", lastname=");
builder.append(lastname);
builder.append("]");
return builder.toString();
}
}
A DAO interface Dao<T>
package com.example.dao;
public interface Dao<T> {
T save(T t) throws Exception;
T save(T t, boolean isRollback) throws Exception;
}
Also, DAO class PersonDAOImpl
package com.example.dao;
import javax.persistence.EntityManager;
import com.example.entities.Person;
public class PersonDAOImpl implements Dao<Person> {
private EntityManager entityManager;
public PersonDAOImpl(EntityManager em) {
this.entityManager = em;
}
#Override
public Person save(Person person) throws Exception {
try {
entityManager.getTransaction().begin();
if (person.getId() == null) {
entityManager.persist(person);
}
entityManager.persist(person);
entityManager.getTransaction().commit();
} catch (Exception e) {
if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
throw e;
} finally {
entityManager.close();
}
return person;
}
#Override
public Person save(Person person, boolean isRollback) throws Exception {
try {
entityManager.getTransaction().begin();
if (isRollback){
entityManager.getTransaction().setRollbackOnly();
}
if (person.getId() == null) {
entityManager.persist(person);
}
if (entityManager.getTransaction().getRollbackOnly()){
System.out.println("Rollback...");
entityManager.getTransaction().rollback();
} else {
entityManager.getTransaction().commit();
}
} catch (Exception e) {
if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
throw e;
} finally {
entityManager.close();
}
return person;
}
}
A service interface PersonService
package com.example.services;
import com.example.entities.Person;
public interface PersonService {
Person savePerson(Person p) throws Exception;
Person savePerson(Person p, boolean isRollback) throws Exception;
}
A service class PersonServiceImpl
package com.example.services;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import com.example.dao.PersonDAOImpl;
import com.example.entities.Person;
#Transactional
public class PersonServiceImpl implements PersonService{
private PersonDAOImpl dao;
public PersonServiceImpl(EntityManager em) {
// DAO
this.dao = new PersonDAOImpl(em);
}
#Override
public Person savePerson(Person p) throws Exception {
return this.dao.save(p);
}
#Override
public Person savePerson(Person p, boolean isRollback) throws Exception {
return this.dao.save(p, isRollback);
}
}
Persistence Configurations
src/test/resources/hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://192.168.1.5:3306/entitymanager-test-001</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- JDBC Connection Pool Property : -->
<property name="hibernate.connection.pool_size">2</property>
<!-- Echo all executed SQL to stdout -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<!-- SQL Dialect Property -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Set the current session context -->
<property name="current_session_context_class">thread</property>
<!-- Entities -->
<mapping class="com.example.entities.Person" />
</session-factory>
</hibernate-configuration>
src/test/resources/META-INF/persistence.xml
<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="my-persistence-unit"
transaction-type="RESOURCE_LOCAL">
<!-- Persistence provider -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.ejb.cfgfile"
value="./hibernate.cfg.xml" />
<property name="hibernate.session.events.log" value="true" />
</properties>
</persistence-unit>
</persistence>
Finally, a JUnit test PersonServiceImplTest
package services;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.transaction.Transactional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.example.entities.Person;
import com.example.services.PersonServiceImpl;
#Transactional
class PersonServiceImplTest {
private EntityManagerFactory emf;
private EntityManager em;
#BeforeEach
void setUp() {
this.emf = Persistence.createEntityManagerFactory("my-persistence-unit");
}
#AfterEach
void tearDown() {
emf.close();
}
#Test
#Transactional
void savePersonRollback() {
this.em = emf.createEntityManager();
// Operation
PersonServiceImpl personService = new PersonServiceImpl(this.em);
// New person
Person person = new Person();
person.setName("Michael");
person.setLastname("Fisher");
Person pe = null;
try {
pe = personService.savePerson(person, true);
assertNotNull(pe);
assertTrue(pe.getId() > 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
New solution attempt (Added: May 17th, 2019)
Add new property person in PersonDAOImpl
Create a new method savePersonDoWork (using Savepoint, PreparedStatement, Work by Hibernate)
package com.example.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
import javax.persistence.EntityManager;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import com.example.entities.Person;
public class PersonDAOImpl implements Dao<Person> {
private EntityManager entityManager;
private Person person;
public PersonDAOImpl(EntityManager em) {
this.entityManager = em;
}
// ... more code
#Override
public Person savePersonDoWork(Person personToSave, boolean isRollback) throws Exception {
try {
entityManager.getTransaction().begin();
if (isRollback) {
System.out.println("Rollback if...");
entityManager.getTransaction().setRollbackOnly();
}
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection con) throws SQLException {
// do something useful
con.setAutoCommit(false);
// Savepoint
Savepoint saveBeforeInsert = con.setSavepoint();
// INSERT SQL
String sql = "INSERT INTO Person (lastname, name) VALUES (?, ?)";
con.prepareStatement(sql);
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, personToSave.getName());
st.setString(2, personToSave.getLastname());
// execute the preparedstatement insert
Long idPerson = new Long(st.executeUpdate());
if (isRollback) {
System.out.println("Rollback execute()...");
con.rollback(saveBeforeInsert);
}
// Close
st.close();
// Set data
person = new Person();
person.setId(idPerson);
person.setName(personToSave.getName());
person.setLastname(personToSave.getLastname());
}
});
} catch (Exception e) {
if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
throw e;
} finally {
entityManager.close();
}
return this.person;
}
}
Unfortunately, the rollback database operation is not working yet.
Expected results
I expect that the database doesn't have any changes, not new rows, not new data, because my database is filling with test data, I don't want that.
Before run the test: SELECT * FROM Person (5 rows)
After run the test: SELECT * FROM Person (5 rows)
Can you help me, Please? I'll appreciate your advice, recommendations or support. Thanks.
You are calling entityManager.getTransaction().commit(); in the DAO. So the transaction is already commited and cannot be rollbacked anymore.
If you want to be able to rollback a transaction you have externalize the transaction handling.
Are you using Spring or Java EE or do you just us plain Hibernate without any other framework?
I'm trying to create a small project with hibernate, but i got that error "Type is not mapped [select o from Type o]", I added mapping in hibernate.cfg.xml but still error.
Type.java:
package com.formation.gestionprojet.doa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="Type")
public class Type implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String name;
private String description;
private String active;
public Type() {
super();
// TODO Auto-generated constructor stub
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getActive() {
return active;
}
public void setActive(String active) {
this.active = active;
}
}
hibernate.org.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- database connection setting -->
<property name ="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/gestion_projet?createDatabaseIfNotExist=true</property>
<property name="connection.username">root</property>
<property name= "connection.password">root</property>
<!-- Dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</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>
<!-- Drope and re-create the database -->
<property name="hbm2ddl.auto">update</property>
<!-- mapping -->
<mapping class= "com.formation.gestionprojet.doa.entity.Type"/>
</session-factory>
</hibernate-configuration>
hibernateUtil.java:
package com.formation.gestionprojet.utils;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
#SuppressWarnings("deprecation")
public class HibernateUtil
{
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
static
{
try
{
Configuration configuration = new Configuration();
configuration.configure("config/hibernate.cfg.xml");
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
catch (HibernateException ex)
{
System.err.println("Error creating Session: " + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory()
{
return sessionFactory;
}
public static Session openSession()
{
return sessionFactory.openSession();
}
public static Session getCurrentSession()
{
return sessionFactory.getCurrentSession();
}
public static void close(){
if(sessionFactory!=null){
sessionFactory.close();
}
}
}
Test.Java
package com.formation.gestionprojet.utils;
import org.hibernate.Session;
public class Test {
static Session session = HibernateUtil.openSession();
public static void main(String[] args) {
session.createQuery("select o from Type o").list();
}
}
solved ! the problem was the version of hibernate that I used so I change it, a change in HibernateUtil.
I'm struggling with an error in my Java project.
Running my code in a local project without any connection to GitHub, everything works fine. As soon as I run the Junit4 test of the same code in a project connected to GitHub, this error occurs:
Java.lang.IllegalArgumentException: An exception occured while creating a query in EntityManager:
Exception Description: Error compiling the query SELECT entity FROM DoSomething entity. Unknown entity type DoSomething.
I am using Eclipse, EclipseLink, PostgreSQL and JPA. The Database is on my localhost. Might there be any problems to find localhost while running the Git Project? I have a local repository for the GitProject so I can't imagine that this is really a problem.
This is the persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="myDatabase" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/myDatabase" />
<property name="javax.persistence.jdbc.user" value="postgres" />
<property name="javax.persistence.jdbc.password" value="secret" />
<property name="eclipselink.ddl-generation.output-mode"
value="database" />
<shared-cache-mode>NONE</shared-cache-mode>
<property name="eclipselink.logging.level" value="off"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
</properties>
</persistence-unit>
*EDIT:
This is my entity class.
import javax.persistence.*;
#Entity
#NamedQueries({
#NamedQuery(name = "findByName", query = "SELECT r FROM DoSomething r WHERE r.name = :name"),
#NamedQuery(name = "findByPreis", query = "SELECT r FROM DoSomething r WHERE r.preis = :preis"),
})
public class DoSomething {
#Id
#GeneratedValue
private int id;
private String name;
private double preis;
public DoSomething() {
}
public DoSomething(String name, double preis) {
this.name = name;
this.preis = preis;
}
public DoSomething(String name){
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPreis() {
return preis;
}
public void setPreis(double preis) {
this.preis = preis;
}}
I exported the entity class into a jar-file. Then I opened a new project where the jar-file is referenced. In this project there is a class that persists the entity to the database. Due to a performance requirement I have to separate the entity and the persisting class into two projects.
This ist the persisting class:
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import demo.persistence.jpa.JpaUtil;
public class DoSomethingDAO {
public void save(DoSomethingDAO entity) throws Exception {
EntityManager em = JpaUtil.createEntityManager();
try {
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
} catch (Exception e) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
throw e;
} finally {
em.close();
}
}
public List<DoSomethingDAO> findByName(String name) throws Exception {
EntityManager em = Persistence.createEntityManagerFactory("myDatabase").createEntityManager();
List<DoSomethingDAO> bList = new ArrayList<DoSomethingDAO>();
try {
TypedQuery<DoSomethingDAO> tQuery = em.createNamedQuery("findByName", DoSomethingDAO.class);
tQuery.setParameter("name", name);
bList = tQuery.getResultList();
em.close();
} catch (Exception e) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
e.printStackTrace();
}
return bList;
}
}
Looks like your persistence.xml not on the classpath while running in tests. Tests scope has another classpath then main one. But I don't know how to set up this in eclipse.
I added
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<class>packagename.DoSomehting</class>
for all classes to the xml-File. Works fine now.
Queries in the form of an entity class A in the form of
SELECT a from A a WHERE TYPE(a) = A
fail with
could not resolve property: class of: de.richtercloud.type.operator.nonsense.A [SELECT a from de.richtercloud.type.operator.nonsense.A a WHERE TYPE(a) = A]
which I don't understand because restricting A to A should exclude all superclass instances which aren't As as well as subclass instances.
Example:
package de.richtercloud.type.operator.nonsense;
import java.io.File;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
/**
* Illustrates the problem/misunderstanding that storing an {#link A} and
* querying it with a {#code WHERE TYPE([identifier]) = A} fails with
* {#code org.hibernate.QueryException: could not resolve property:
* class of: de.richtercloud.type.operator.nonsense.A}.
* #author richter
*/
public class NewMain {
private final static File DATABASE_DIR = new File("/tmp/type-operator-nonsense");
private final static String DERBY_CONNECTION_URL = String.format("jdbc:derby:%s", DATABASE_DIR.getAbsolutePath());
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws SQLException {
//setup database
EntityManagerFactory entityManagerFactory = null;
try {
Map<Object, Object> entityManagerFactoryMap = new HashMap<>();
entityManagerFactoryMap.put("javax.persistence.jdbc.url",
String.format("%s;create=%s", DERBY_CONNECTION_URL, !DATABASE_DIR.exists()));
entityManagerFactory = Persistence.createEntityManagerFactory("type-operator-nonsense",
entityManagerFactoryMap);
//show issue
EntityManager entityManager = entityManagerFactory.createEntityManager();
A a = new A(1L, "b");
entityManager.getTransaction().begin();
entityManager.persist(a);
entityManager.flush();
Query query = entityManager.createQuery("SELECT a from A a WHERE TYPE(a) = A");
List<?> queryResult = query.getResultList();
entityManager.getTransaction().commit();
System.out.println(queryResult.size());
}finally {
if(entityManagerFactory != null) {
entityManagerFactory.close();
}
}
}
}
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="type-operator-nonsense" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>de.richtercloud.type.operator.nonsense.A</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:derby:/tmp/type-operator-nonsense"/>
<property name="javax.persistence.jdbc.user" value=""/>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
with org.hibernate:hibernate-entitymanager:5.1.0.Final and org.apache.derby:derby:10.11.1.1
A is a POJO in this example which doesn't involve inheritance, that shouldn't matter for the restriction I expect (although it wouldn't make sense to apply it if there's no inheritance):
package de.richtercloud.type.operator.nonsense;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class A implements Serializable {
private static final long serialVersionUID = 1L;
private String b;
#Id
private Long id;
public A() {
}
public A(Long id, String b) {
this.id = id;
this.b = b;
}
public void setB(String b) {
this.b = b;
}
public String getB() {
return b;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
This behaviour occurs due to a hibernate bug https://hibernate.atlassian.net/browse/HHH-10653. The example above works fine with OpenJPA.
TYPE() can only be used in inheritance mapping.
Is A a class in a inheritance mapping and A either the route class with #Inheritance or A extends a class that has #Inheritance declared?
Example.
#Inheritance
#Entity
public class Project
#Entity
public class DesignProject extends Project
#Entity
public class QualityProject extends Project
#Entity
public class SoftwareProject extends Project
Now you can use TYPE()
SELECT p
FROM Project p
WHERE TYPE(p) = DesignProject OR TYPE(p) = QualityProject
I try to use jpa with spring-orm and hibernate.
I have mysql db with TRADES table. I try to work with this table using jpa.
I try to test EntityManager creation using persistence.xml but I get an exception that "No Persistence provider for EntityManager named [persistence-unit-name from persistence.xml]"
As I watched in another such questions, it's mean for my situation that persistence.xml file is not found.
When I try to test this configuration I get an exception that persistence.xml wasn't find, as I think.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named trade-mysql-pu
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)
at com.madhusudhan.jsd.jpa.EntityManagerTest.init(EntityManagerTest.java:16)
at com.madhusudhan.jsd.jpa.EntityManagerTest.main(EntityManagerTest.java:29)
I can't figure out why this is happening. Could you help me?
Thanks.
Table:
create table TRADES (ID int NOT NULL,
ACCOUNT VARCHAR(20) NOT NULL,
SECURITY VARCHAR(10) NOT NULL,
QUANTITY INT NOT NULL,
STATUS VARCHAR(10),
DIRECTION VARCHAR(10)
);
entity class for this table:
package com.madhusudhan.jsd.domain.jpa;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="trades")
public class Trade {
private int id;
private String direction;
private String account;
private String security;
private String status;
private int quantity;
#Column(nullable=false)
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getDirection() {
return direction;
}
public void setDirection(String direction) {
this.direction = direction;
}
#Column
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
#Column
public String getSecurity() {
return security;
}
public void setSecurity(String security) {
this.security = security;
}
#Column
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Column
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
#Override
public String toString() {
return "Trade [id=" + id + ", direction=" + direction + ", account="
+ account + ", security=" + security + ", status=" + status
+ "]";
}
}
And persistence.xml in src/main/resources/META-INF
<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="trade-mysql-pu" transaction-type="RESOURCE_LOCAL">
<provider>org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter</provider>
<class>com.madhusudhan.jsd.domain.jpa.Trade</class>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="show_sql" value="true" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/JSDATA"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="prospring4"/>
<property name="hibernate.connection.password" value="prospring4"/>
<property name="hibernate.connection.pool_size" value="1"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>
</persistence>
This is the test class:
package com.madhusudhan.jsd.jpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import com.madhusudhan.jsd.domain.jpa.Trade;
public class EntityManagerTest {
private EntityManagerFactory factory;
private EntityManager entityManager;
private void init() {
factory = Persistence.createEntityManagerFactory("trade-mysql-pu");
entityManager = factory.createEntityManager();
}
public void persistTrade(Trade t) {
entityManager.persist(t);
}
public static void main(String[] args) {
EntityManagerTest test = new EntityManagerTest();
test.init();
}
}
dependencies from build.gradle:
dependencies {
compile 'org.springframework:spring-context:4.1.6.RELEASE'
compile 'org.springframework:spring-jdbc:4.1.6.RELEASE'
compile 'org.springframework:spring-orm:4.1.6.RELEASE'
compile 'commons-dbcp:commons-dbcp:1.4'
compile 'mysql:mysql-connector-java:5.1.18'
compile 'org.hibernate:hibernate-core:3.6.0.Final'
compile 'org.hibernate:hibernate-entitymanager:3.6.0.Final'
compile 'junit:junit:4.7'
compile 'log4j:log4j:1.2.14'
}
Few things you can try:
Check that your src/main/resources folder is in your classpath. In Eclipse, it means right click on the project > Properties > Java Build Path and make sure that src/main/resources is specified under Source tab. If it is not there, click Add Folder to add resources folder.
If the above does not work, try to move your persistence.xml to src/main/java/META-INF folder.
Change to provider in persistence.xml to
org.hibernate.ejb.HibernatePersistence