How to generate the data schema with Hibernate on PostgreSQL 13 - java

I am working on a new project with postgreSQL 13 and the auto generation of the data schema does not work. My approach is to use annotations on my entities as I always did before.
Here is one of my entities and my persistence.xml file:
#Entity
public class User implements Serializable {
#OneToOne
private Agent agent;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idUser;
private ProfilUser profilUser;
private LocalDate dateCreation;
private LocalDate dateActivation;
private LocalDate date_desactivation;
private LocalDate date_premiereConnexion;
#ManyToMany
private List <ProfilUser> userProfils;
public Long getId() {
return idUser;
}
public void setId(Long id) {
this.idUser = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (idUser != null ? idUser.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the idUser fields are not set
if (!(object instanceof User)) {
return false;
}
User other = (User) object;
if ((this.idUser == null && other.idUser != null) || (this.idUser != null && !this.idUser.equals(other.idUser))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.weyetech.gestionstock.entities.User[ id=" + idUser + " ]";
}
public LocalDate getDateCreation() {
return dateCreation;
}
public void setDateCreation(LocalDate dateCreation) {
this.dateCreation = dateCreation;
}
public LocalDate getDateActivation() {
return dateActivation;
}
public void setDateActivation(LocalDate dateActivation) {
this.dateActivation = dateActivation;
}
public LocalDate getDate_desactivation() {
return date_desactivation;
}
public void setDate_desactivation(LocalDate date_desactivation) {
this.date_desactivation = date_desactivation;
}
public LocalDate getDate_premiereConnexion() {
return date_premiereConnexion;
}
public void setDate_premiereConnexion(LocalDate date_premiereConnexion) {
this.date_premiereConnexion = date_premiereConnexion;
}
public Long getIdUser() {
return idUser;
}
public void setIdUser(Long idUser) {
this.idUser = idUser;
}
public ProfilUser getProfilUser() {
return profilUser;
}
public void setProfilUser(ProfilUser profilUser) {
this.profilUser = profilUser;
}
public List<ProfilUser> getUserProfils() {
return userProfils;
}
public void setUserProfils(List<ProfilUser> userProfils) {
this.userProfils = userProfils;
}
}
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://java.sun.com/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_2.xsd">
<!-- Define Persistence Unit -->
<persistence-unit name="g_stock" transaction-type="JTA">
... <!-- Entities -->
<properties>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect"/>
</properties>
</persistence-unit>
</persistence>
I tried to change the dialect of postgre to 10 (since it is the dialect of version 10 and later) but without success.

I recommend not using the auto generate function of Hibernate as it creates unmaintainable database objects. If you are having issues and you need to manually check stuff in the database you will have trouble figuring out stuff.
I personally would use liquibase or FlywayDB to handle the database migrations. Both are excellent products.
To quote (from point 8 here):
Hibernate can use the mapping information of your entities to generate
a database schema. That’s the easiest approach, and you can see it in
several examples on the internet. That might be OK for a small test
application, but you shouldn’t use it for a business application. The
database schema has a huge influence on the performance and size of
your database. You, therefore, should design and optimize the database
schema yourself and export it as an SQL script. You can run this
script either with an external tool like Flyway or you can use
Hibernate to initialize the database at startup. The following snippet
shows a persistence.xml file which tells Hibernate to run the
create.sql script to setup the database. You can learn more about the
different configuration parameters in Standardized schema generation
and data loading with JPA 2.1.

You probably need to use the new official properties for schema generation.
<property name="javax.persistence.schema-generation.create-database-schemas" value="true"/>
Or try different settings from this nice overview

Related

com.sun.jdi.invocationexception on method getOne(id) Hibernate + Spring MVC + JPA Repository

Im doing a Spring MVC project and im stucked in this problem.
I have an Entity "TipoDoc", his own Service and Repository using JPA Repository, in my service I have a getAll() method than calls the findAll() JPA Repository method and it works fine, but when I want to use a method to get one by id, im receiving a null object whatever id I send to the method.
So, I started to debug searching the problem and I found a com.sun.jdi.invocationexception in response when Hibernate have to execute the method getOne() from JPA Repository.
I dont know what is wrong in my code, or how can I get more details from the exception.. Im using log4j for loging but i dont know how catch that exception in the log..
Im using MySql Database
Here is my code
#Entity
#Table(name = "TiposDocumento")
public class TipoDoc
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idTipoDocumento")
private long id;
private String descripcion;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}
The Service
#Service
public class TipoDocService {
private final TipoDocRepo tipoDocRepo;
#Autowired
public TipoDocService(TipoDocRepo tipoDocRepo) {
this.tipoDocRepo = tipoDocRepo;
}
public List<TipoDoc> getAll() {
return (List<TipoDoc>)tipoDocRepo.findAll();
}
public TipoDoc getById(Long id) {
return (TipoDoc) tipoDocRepo.getOne(id);
}
}
The Repository
public interface TipoDocRepo extends JpaRepository<TipoDoc, Long>{
}
The Controller
#Controller
public class ClientController
{
private static final Logger logger = Logger.getLogger(ClientController.class);
private final ClienteService clienteService;
private final TipoDocService tipoDocService;
private final EstadoCivilService estadoCivilService;
private final ProvinciaService provinciaService;
private final LocalidadService localidadService;
private final CondIvaService condIvaService;
#Autowired
public ClientController(ClienteService clienteService, TipoDocService tipoDocService, EstadoCivilService estadoCivilService,
ProvinciaService provinciaService, LocalidadService localidadService, CondIvaService condIvaService) {
this.clienteService = clienteService;
this.tipoDocService = tipoDocService;
this.estadoCivilService = estadoCivilService;
this.provinciaService = provinciaService;
this.localidadService = localidadService;
this.condIvaService = condIvaService;
}
#RequestMapping("/Clientes")
public ModelAndView formularioCliente()
{
ModelAndView mav = new ModelAndView("clientes");
mav.getModel().put("cliente",new Cliente());
mav.getModel().put("tiposDoc", tipoDocService.getAll()); //Works fine, tiposDoc={{1,DNI};{2,Passaport};{3,LC}}
TipoDoc tipoDoc = tipoDocService.getById((long) 1); //not working tipoDoc={0,null} when it have to be {1,DNI}
mav.getModel().put("estadosCiviles", estadoCivilService.getAll());
mav.getModel().put("provincias", provinciaService.getAll());
mav.getModel().put("localidades", localidadService.getAll());
mav.getModel().put("condicionesIva", condIvaService.getAll());
return mav;
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="2.1">
<persistence-unit name="OFYS">
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="admin" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/OFYS" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
</properties>
</persistence-unit>
</persistence>
And this is what can I find debuging
EDIT.
Here is the full description from the exception I can get if u dont see the image
com.sun.jdi.invocationexception: Exception ocurred in target VM ocurred invoking method.
The object does not exist in the database with the given id. Apparently you have a proxy of that object in your persistence context which is returned here. When accessing the object it tries to actually load it from the database and fails because there is no row with that id.

JUnit error "Unknown entity type" when using entities from a jar-file

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.

persistence and Hibernate errors in spring boot JPA

persistenceUnit/Hibernate
Connection to persistenceUnit/Hibernate failed
Could not create connection to database server. Attempted reconnect 3 times
I have this error and when i go to jpa console and run commands, i get this:
access denied for user 'root'#'localhost' (using password: YES)
Despite, i can see my database in database tab in intellij. And also it created entityclass.
So, why do i have those errors?
It did not create persistance.xml so i created it and put to src/web-inf
<persistence-unit name="persistenceUnit">
<class>models.MovieEntity</class>
<properties>
<property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipselink.jdbc.user"/>
<property name="eclipselink.jdbc.password"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username"/>
<property name="hibernate.connection.password"/>
<property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
<property name="openjpa.ConnectionUserName"/>
<property name="openjpa.ConnectionPassword"/>
<property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="toplink.jdbc.user"/>
<property name="toplink.jdbc.password"/>
</properties>
</persistence-unit>
it generated those lines but sone of them are red, errors. SAme password for all for example?
Also, i get those errors:
ava.lang.RuntimeException: org.hibernate.AnnotationException: No identifier specified for entity: com.models.MovieEntity
at org.hibernate.cfg.InheritanceState.determineDefaultAccessType(InheritanceState.java:277)
at org.hibernate.cfg.InheritanceState.getElementsToProcess(InheritanceState.java:224)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:775)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3845)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3799)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1412)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
my entity
#Entity
#Table(name = "movie", schema = "mysql", catalog = "")
public class MovieEntity {
private int id;
private String title;
private String actors;
#Basic
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Id
#Column(name = "Title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Basic
#Column(name = "Actors")
public String getActors() {
return actors;
}
public void setActors(String actors) {
this.actors = actors;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MovieEntity that = (MovieEntity) o;
if (id != that.id) return false;
if (title != null ? !title.equals(that.title) : that.title != null) return false;
if (actors != null ? !actors.equals(that.actors) : that.actors != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + (actors != null ? actors.hashCode() : 0);
return result;
}
}
entity class gives error of no catalog.
Seen the your configuration I can see that you have the username and password empty, now probably the persistance.xml that you are posted was a example bat one of your error access denied for user 'root'#'localhost' (using password: YES) follow me to advice you to check if your database was configured with a password and use it (I'm not crazy in a standard installation without configuration on linux many times I had empty password). Then I can see that you have #Id on title properties when probably the your sql for tabel generation, requires that id was the primary key.
I hope that this can help you
The database seems to be missing in the connection string
Most likely your Entity has no field annotated with #Id
Your actual error is :
No identifier specified for entity: com.models.MovieEntity
You must specify an id in com.models.MovieEntity class
#javax.persistence.Id
private Long id;

Hibernate session.get() shows null

Although I read session.get null problems on stackoverflow and tried to do what they suggested, my problem seems to continue.
SessionFactory mysessionfactory = new Configuration().configure().buildSessionFactory();
Session mysession = mysessionfactory.openSession();
mysession.beginTransaction();
Myperson person3 = (Myperson) mysession.get(Myperson.class, 3);
System.out.println("there you go : "+person3.getName());
As simple as it seems, i am just trying to retrieve data from database. Let me show you the database.
As you can see above , the data i like to retrieve has a name !
This is what i get, NOTHING !
And finally , i run the code with debug mode and i hope you'll understand the problem and suggest a solution.
i am in a conflict here, my database shows that data which has UserId of 3 has a name but why can't i see it in my output ?
EDIT : Here is my hibernate xml file as you requested.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.pool.size">1</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/myhibernatedb
</property>
<property name="hibernate.connection.username">
root
</property>
<property name="hibernate.connection.password">
000003
</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<!-- List of XML mapping files -->
<mapping class="org.emreverim.com.Myperson"/>
<mapping class="org.emreverim.com.Vehicle" />
<mapping class="org.emreverim.com.FourWheeler" />
<mapping class="org.emreverim.com.TwoWheeler" />
</session-factory>
</hibernate-configuration>
EDIT 2 : here is myperson class as you requested.
#Entity
public class Myperson {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int userID;
private String name;
#OneToMany(cascade=CascadeType.PERSIST)
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
public Collection<Vehicle> getVehicle() {
return vehicle;
}
public void setVehicle(Collection<Vehicle> vehicle) {
this.vehicle = vehicle;
}
#Lob
private String description;
#Temporal (TemporalType.DATE)
private Date joinedDate;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getJoinedDate() {
return joinedDate;
}
public void setJoinedDate(Date joinedDate) {
this.joinedDate = joinedDate;
}
public int getUserID() {
return userID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setUserID(int userID){
this.userID = userID;
}
}
I think the problem here is caused by the fact that Hibernate Session.get() method expects a Seriliazable id object, which is not provided in your case here mysession.get(Myperson.class, 3) .
Because the given value 3 here is of primitive type int which contains only the value and isn't serialized so you have to use new Integer() method in order to pass an Integer serializable object or new Long() to pass a Long serializable object to the get() method, so just change:
Myperson person3 = (Myperson) mysession.get(Myperson.class, 3);
To:
Myperson person3 = (Myperson) mysession.get(Myperson.class, new Integer(3));
Or:
Myperson person3 = (Myperson) mysession.get(Myperson.class, new Long(3));
We happened to see the solution, my database has been configured as utf-16 while it should have been utf-8. so its just as simple as that. i gave thumps up for all answers, thanks for all of your concerns.
In your entity map you forgot to add the annotations #column attributes to the field and #table to link it to the table.
Example:
#Column(name="description")
private String description;
Check this example for the annotations http://archive.oreilly.com/pub/a/onjava/2007/02/08/an-introduction-to-hibernate-3-annotations.html?page=2

MultiTenancy with Hibernate 4.0 with Separate Schema approach

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.

Categories

Resources