I have been learning to use hibernate for a couple of months.
I am finding it difficult in deciding how to configure hibernate to work on a test database.
I have a hibernate.cfg.xml with db parameters given as elements.
<property name="connection.url">
jdbc:postgresql://localhost/mydb
</property>
<property name="connection.username">me</property>
<property name="connection.password">mypasswd</property>
My web app uses a HibernateUtil class which loads the configuration as below
class HibernateUtil {
private Class<T> persistentClass;
private static SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
}catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
...
My dao implementation uses the above class to get the Session
public class BaseDaoImpl<T, ID extends Serializable>{
private Session session;
...
public Session getSession() {
if (session == null || !session.isOpen()){
return HibernateUtil.getCurrentSession();
}else{
return session;
}
}
public T findById(ID id){
T entity = (T) getSession().load(getPersistentClass(), id);
return entity;
}
This is OK as long as I work on the mydb configured in cfg.xml file.But for my tests I am using another database which is given in a test.properties file
test.db.url=jdbc:postgresql://localhost/mytestdb
test.db.driver=org.postgresql.Driver
test.db.username=testme
test.db.password=testpass
The only way I can make hibernate work on mytestdb is to manually replace every db related property in cfg.xml.I would very much like to use test_hibernate.cfg.xml with the test db properties,but since the configuration is done in a static block in HibernateUtil ,that won't work.
Is there a better way of configuring hibernate for these two databases?
I am using ant as build tool..
Any suggestions would be most welcome
jim
I would very much like to use test_hibernate.cfg.xml with the test db properties,but since the configuration is done in a static block in HibernateUtil
So then don't create your configuration in a static block.
Instead, create a class which accepts a parameter for the path to the configuration file (a la Spring's LocalSessionFactoryBean) which returns the Configuration/SessionFactory to use. Or if you truly want to stick with HibernateUtil (which is a strategy very much recommended against in any production setting), change it to read a property or system environment variable to read the configuration.
Related
I'm using Hibernate withhout #annotations
I tried this code:
public class HibernateUtil {
private static final SessionFactory sessionFactory;
private static StandardServiceRegistryBuilder builder;
static {
try {
Configuration configuration = new Configuration().configure();
configuration.configure("hibernate.cfg.xml");
builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
sessionFactory = configuration.buildSessionFactory(builder.build());
} catch (HibernateException ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Client code:
public static void main (String[] args) {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> customers = criteria.list();
for (Customer customer : customers) {
//print values
}
session.getTransaction().commit();
session.close();
}
Also here is important thing I want to know
What if I will create HibernateUtil class object in Client class?
HibernateUtil hibernateUtil = new HibernateUtil();
You can refer stackoverflow link:
What are the advantages and disadvantages of creating an Object in static block in Java?.
If you want SessionFactory to be created when your server or application starts, then static block can be used. else, go for static method approach. You can also use enums to create singleton.
So, you've got two questions there, one about this being the right approach to create a singleton, and the other about what happens if you create a HibernateUtil instance in the client.
First for the HibernateUtil
For the second question, you're fine if each client creates an instance of the HibernateUtil class, as the variable is static. If all goes according to plan, there will only be one Hibernate SessionFactory instance.
As for whether you have a correct Hibernate SessionFactory singleton implementation? That's a harder question to answer. While your code looks good, multiple classloaders can cause all sorts of unpredictable problems with singletons. If separate classloaders create an instance, you may have multiple instances of your singleton, a paradox you want to avoid.
EJB and Spring Singleton help
With the EJB specification, you can mark a session bean as a singleton. If you are using a Java web profile compliant server, I'd do that. If you are using Spring Boot, use the singleton facilities they provide. If it's a standalone application, keep an eye out for peculiar, non-singleton SessionFactory behavior.
I'm using Spring and Hibernate with an automatically generated database (for that I have set "hibernate.hbm2ddl.auto" to "update" in the JPA configuration properties).
I also have a class annotated #Configuration with a #PostConstruct method that is called on application startup after the database has been created or updated. This is where I setup the database with some default data if it's empty (first launch).
I would like to execute some custom native SQL queries at this moment. These queries won't return anything, they're just configuration stuff (like creating additional indexes or extensions).
Currently I'm stuck on creating a SessionFactory in order to create a new Hibernate Session. I've tried auto wiring it, but it doesn't work :
#Autowired
SessionFactory sessionFactory;
Gives me: Field sessionFactory in ... required a bean of type 'org.hibernate.SessionFactory' that could not be found.
I understand that I probably need to configure it elsewhere, but I don't know where. Several answers on SO use an xml configuration file, but I'm not using any configuration file so I can't do it that way.
Is there a way Spring can create the SessionFactory with the appropriate configuration ?
You don't even need to access SessionFactory. Please just put your scripts into a file src/main/resources/scripts/myscript.sql. You can then do the following with Spring:
#Component
public class Startup {
#Autowired
private DataSource dataSource;
#PostConstruct
public void runNativeSql() {
ClassPathResource resource = new ClassPathResource("scripts/myscript.sql");
try(Connection connection = dataSource.getConnection()) {
ScriptUtils.executeSqlScript(connection, resource);
} catch (SQLException | ScriptException e) {
//LOG
}
}
}
You can autowire the JPA EntityManager as:
#PersistenceContext
EntityManager entityManager;
If you really need a Hibernate Session and are using using JPA 2.1, the Session can be obtained from the EntityManager as:
entityManager.unwrap(Session.class);
Maybe what I´m going to ask it´s a silly question, what I wan to know if it is possible in a Spring MVC configuration has two entityManagerFactory. I will explain why.
I have one LocalContainerEntityManagerFactoryBean where I configure a hibernate.tenant_identifier_resolver which I use to determine the tenant by LDAP using the session of the user, and then use one database Schema or another, then I use "multi_tenant_connection_provider" to create the database connection for that schema.
Now my application has a Scheduler that needs access to all Schemas and get some information from all databases. So in order to do not touch the entityManagerFactory already configure, what I was thinking was to create a new one with my own implementation of "hibernate.tenant_identifier_resolver" to control which schema I want instead LDAP, before create the database connection by "multi_tenant_connection_provider".
the problem looks like Spring do not allow me configure two entityManagerFactory.
Can you give me any advice about how to achieve what I want?
Regards!
Yes, it is possible to use multiple EntityManagers.
In my project I use the annotation configuration, where I have:
#Configuration
#EnableTransactionManagement
public class AppConfig {
#Bean
public SessionFactory smartDataSessionFactory() {
return new LocalSessionFactoryBuilder(smartDataDatasource())
.scanPackages("...)
.addProperties(smartDataHibernateProperties())
.buildSessionFactory();
}
#Bean
public SessionFactory analysisSessionFactory() {
return new LocalSessionFactoryBuilder(analysisDatasource())
.scanPackages("...)
.addProperties(analysisHibernateProperties())
.buildSessionFactory();
}
...
}
When referencing the entityManagers, be sure to use the Qualifier annotation.
Also note that each SessionFactory will use it's own TransactionFactory
#Repository
#Transactional(value = "analysisTransactionManager")
public class ToURemunerationDaoImpl implements ToURemunerationDao {
private SessionFactory analysisSessionFactory;
private SessionFactory smartDataSessionFactory;
#Autowired
#Qualifier("analysisSessionFactory")
public void setAnalysisSessionFactory(SessionFactory sessionFactory) {
this.analysisSessionFactory = sessionFactory;
}
#Autowired
#Qualifier("smartDataSessionFactory")
public void setSmartDataSessionFactory(SessionFactory sessionFactory) {
this.smartDataSessionFactory = sessionFactory;
}
...
}
Finally I found the solution. The issue was becuase I´m using Spring data, and my repositories did not which EntityManagerFactory use. As soon as I specify which one to use during the scan everything works like a charm.
<repositories base-package="com.*.*.repository**" entity-manager-factory-ref="entityManagerFactory"/>
I have developed a web application using Spring MVC + Hibernate and when running load tests it seems the Garbage Collector is being called too often. I am afraid it might have to do with the way I manage Hibernates session.
I have an AbstractDao which all of my DAO objects extends:
public class AbstractDaoHibernateImpl {
protected GenericDataBaseExceptionHandler exceptionHandler;
private SessionFactory sessionFactory;
public AbstractDaoHibernateImpl() {
}
public void setExceptionHandler(GenericDataBaseExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
}
In spring entity bean configuration I define each of my DAOs like this:
<bean name="userDao" class="com.kelmer.dao.UserDaoImpl">
<property name="sessionFactory" ref="SessionFactory" />
<property name="exceptionHandler" ref="defaultSpringExceptionHandler" />
</bean>
And then this is a sample method from one of my DAOs:
#Override
public EstacionVO findById(Long id) throws InstanceNotFoundException {
User e = (User ) getSession().createQuery(SELECT_USER_BY_ID).setParameter("userId", id).uniqueResult();
if (e == null) {
throw new InstanceNotFoundException ("No user with provided Id", User.class);
}
return e;
}
I honestly can see anything that might be causing a memory leak but then again I'm no real expert here. Am I doing session management right? I know I took the code from AbstractDao from some legacy code and I fear that's where the memory problem could be, since there is no explicit session closing or finalizing.
PS. For transaction management, I am using <tx:annotation-driven /> in spring context and annotating each method with #Transactional.
At first glance, there doesn't seem to be anything unusual in your code.
Run a performance monitor to see what objects are created.
Unless the problem is trivial, it's hard to see where performance is lost by just looking at the code and 90% of the time, any statement "it must be this" turns out to be wrong.
What is your hibernate session creation strategy. Is your code using the same session again and again?
Instead of :
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
try opening a new session for each request:
protected Session getSession() {
return sessionFactory.openSession();
}
Each session has a session cache to which all retrieved or persisted objects are added. If you keep on using the same session for querying many-many entities, it 'may' lead to such a scenario.
Give the change a try and do let us know if helps.
The right way
Use one of the performance monitoring tools to see what's really causing the issue:
http://java.dzone.com/articles/java-performance-troubleshooti-0
i'm using spring + hibernate. All my HibernateDAO use directly sessionFactory.
I have application layer -> service layer -> DAO layer and all collections is lazly loaded.
So, the problem is that sometime in the application layer(that contains GUI/swing) i load an entity using a service layer method(that contains #Transactional annotation) and i want to use a lazly property of this object, but obviusly the session is already closed.
What is the best way to resolve this trouble?
EDIT
I try to use a MethodInterceptor, my idea is to write an AroundAdvice for all my Entities and use annotation, so for example:
// Custom annotation, say that session is required for this method
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SessionRequired {
// An AroundAdvice to intercept method calls
public class SessionInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
bool sessionRequired=mi.getMethod().isAnnotationPresent(SessionRequired.class);
// Begin and commit session only if #SessionRequired
if(sessionRequired){
// begin transaction here
}
Object ret=mi.proceed();
if(sessionRequired){
// commit transaction here
}
return ret;
}
}
// An example of entity
#Entity
public class Customer implements Serializable {
#Id
Long id;
#OneToMany
List<Order> orders; // this is a lazy collection
#SessionRequired
public List<Order> getOrders(){
return orders;
}
}
// And finally in application layer...
public void foo(){
// Load customer by id, getCustomer is annotated with #Transactional
// this is a lazy load
Customer customer=customerService.getCustomer(1);
// Get orders, my interceptor open and close the session for me... i hope...
List<Order> orders=customer.getOrders();
// Finally use the orders
}
Do you think can this work?
The problem is, how to register this interceptor for all my entities without do it in xml file?
There is a way to do it with annotation?
Hibernate recently introduced fetch profiles which (in addition to performance tuning) is ideal for solving issues like this. It allows you to (at runtime) choose between different loading and initialization strategies.
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance-fetching-profiles
Edit (added section on how to set the fetch profile using an interceptor):
Before you get started: Check that fetch profiles actually will work for you. I haven't used them myself and see that they are currently limited to join fetches. Before you waste time on implementing and wiring up the interceptor, try setting the fetch profile manually and see that it actually solves your problem.
There are many ways to setup interceptors in Spring (according to preference), but the most straight-forward way would be to implement a MethodInterceptor (see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api-advice-around). Let it have a setter for the fetch profile you want and setter for the Hibernate Session factory:
public class FetchProfileInterceptor implements MethodInterceptor {
private SessionFactory sessionFactory;
private String fetchProfile;
... setters ...
public Object invoke(MethodInvocation invocation) throws Throwable {
Session s = sessionFactory.openSession(); // The transaction interceptor has already opened the session, so this returns it.
s.enableFetchProfile(fetchProfile);
try {
return invocation.proceed();
} finally {
s.disableFetchProfile(fetchProfile);
}
}
}
Lastly, enable the interceptor in the Spring config. This can be done in several ways and you probably already have a AOP setup that you can add it to. See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-schema.
If you're new to AOP, I'd suggest trying the "old" ProxyFactory way first (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html#aop-api-proxying-intf) because it's easier to understand how it works. Here's some sample XML to get you started:
<bean id="fetchProfileInterceptor" class="x.y.zFetchProfileInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="fetchProfile" ref="gui-profile"/>
</bean>
<bean id="businessService" class="x.y.x.BusinessServiceImpl">
<property name="dao" .../>
...
</bean>
<bean id="serviceForSwinGUI"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="x.y.z.BusinessServiceInterface/>
<property name="target" ref="businessService"/>
<property name="interceptorNames">
<list>
<value>existingTransactionInterceptorBeanName</value>
<value>fetchProfileInterceptor</value>
</list>
</property>
</bean>
Create a method in the service layer that returns the lazy-loaded object for that entity
Change to fetch eager :)
If possible extend your transaction into the application layer
(just while we wait for someone who knows what they are talking about)
You need to rework your session management, unfortunately. This is a major problem when dealing with Hibernate and Spring, and it's a gigantic hassle.
Essentially, what you need is for your application layer to create a new session when it gets your Hibernate object, and to manage that and close the session properly. This stuff is tricky, and non-trivial; one of the best ways to manage this is to mediate the sessions through a factory available from your application layer, but you still need to be able to end the session properly, so you have to be aware of the lifecycle needs of your data.
This stuff is the most common complaint about using Spring and Hibernate in this way; really, the only way to manage it is to get a good handle on exactly what your data lifecycles are.