Alter Session to set Date Format in MyBatis - java

How can I do this in the same transactional session?
alter session set nls_date_format = 'yyyy/mm/dd hh24:mi:ss'
There are some procedures and insertes that I need to execute this before them.
I've tried to make another method, in the same one, but it still doesn't work.
I have MyBatis integrated with Spring. I don't know if it makes any difference.
Can anyone help me?
Thanks.
SOLUTION:
I'v managed this to work by changing the way Spring and MyBatis integrates through SqlSession.

Here are two possible solutions.
Extend SqlSessionFactory
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.log4j.Logger;
public class CustomSqlSessionFactory extends DefaultSqlSessionFactory
{
private static Logger msLogger = Logger.getLogger(CustomSqlSessionFactory.class);
public CustomSqlSessionFactory(Configuration configuration)
{
super(configuration);
}
#Override
public SqlSession openSession()
{
SqlSession session = super.openSession();
alterSession(session);
return session;
}
protected void alterSession(SqlSession session)
{
try
{
Statement statement = session.getConnection().createStatement();
statement.addBatch("alter session set nls_date_format = 'yyyy/mm/dd hh24:mi:ss'");
statement.addBatch("ALTER SESSION SET NLS_COMP = LINGUISTIC");
statement.addBatch("ALTER SESSION SET NLS_SORT = XTURKISH_AI");
statement.executeBatch();
msLogger.debug("Altered newly created session parameters.");
statement.close();
}
catch (SQLException e)
{
msLogger.error("Alter session failed!", e);
}
}
#Override
public SqlSession openSession(boolean autoCommit)
{
SqlSession session = super.openSession(autoCommit);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(Connection connection)
{
SqlSession session = super.openSession(connection);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(ExecutorType execType)
{
SqlSession session = super.openSession(execType);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit)
{
SqlSession session = super.openSession(execType, autoCommit);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(ExecutorType execType, Connection connection)
{
SqlSession session = super.openSession(execType, connection);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
{
SqlSession session = super.openSession(execType, level);
alterSession(session);
return session;
}
#Override
public SqlSession openSession(TransactionIsolationLevel level)
{
SqlSession session = super.openSession(level);
alterSession(session);
return session;
}
}
If you are using spring also create a CustomSqlSessionFactoryBuilder
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class CustomSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder
{
#Override
public SqlSessionFactory build(Configuration config)
{
return new CustomSqlSessionFactory(config);
}
}
and attach CustomSqlSessionFactoryBuilder by modifying the SqlSessionFactoryBean configuration
<bean id="mySqlSessionFactoryBuilder" class="your.package.CustomSqlSessionFactoryBuilder" />
<bean id="mySessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="sqlSessionFactoryBuilder" ref="mySqlSessionFactoryBuilder" />
</bean>
Session will be altered on each borrow operation. However, if you are using pooled connection, this approach will degrade the execution performance. Hence on every checkout of the connection from the pool, openSession will be called.
If you are using a pooled data source then handling session alter operations on data source level will be much faster. Second solution modifies C3P0 pooled data source for altering session.
Modify Pooled Data Source (C3P0 and Spring)
Create a connection customizer class [1]
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.Logger;
import com.mchange.v2.c3p0.AbstractConnectionCustomizer;
public class ConnectionCustomizer extends AbstractConnectionCustomizer
{
private static Logger msLogger = Logger.getLogger(ConnectionCustomizer.class);
public void onAcquire(Connection c, String pdsIdt)
{
try
{
Statement statement = c.createStatement();
statement.addBatch("alter session set nls_date_format = 'yyyy/mm/dd hh24:mi:ss'");
statement.addBatch("ALTER SESSION SET NLS_COMP = LINGUISTIC");
statement.addBatch("ALTER SESSION SET NLS_SORT = XTURKISH_AI");
statement.executeBatch();
msLogger.debug("Altered newly created session parameters.");
statement.close();
}
catch (SQLException e)
{
msLogger.error("Alter session failed!", e);
}
}
}
Modify data source configuration bean and add created class as connectionCustomizerClassName
<!--
driverClass : Driver class that will be used to connect to database.
jdbcUrl : jdbc url defining the database connection string.
user : username of the database user.
password : password of the database user.
acquireIncrement : how many connections will be created at a time when there will be a shortage of connections.
idleConnectionTestPeriod : after how much delay a connection will be closed if it is no longer in use.
maxPoolSize : Max number of connections that can be created.
maxStatements : Max number of SQL statements to be executed on a connection.
minPoolSize : Minimum number of connections to be created.
connectionCustomizerClassName : Custom connection customizer to enable session alterations and jobs on acquiring/closing - checking in/out physical connections
-->
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl" value="${app.jdbc.url}" />
<property name="user" value="${app.jdbc.username}" />
<property name="password" value="${app.jdbc.password}" />
<property name="acquireIncrement" value="3" />
<property name="maxPoolSize" value="50" />
<property name="maxStatements" value="50" />
<property name="minPoolSize" value="5" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="preferredTestQuery" value="SELECT 1 FROM DUAL" />
<property name="testConnectionOnCheckout" value="true" />
<property name="connectionCustomizerClassName" value="your.package.name.ConnectionCustomizer" />
</bean>
[1]: http://www.mchange.com/projects/c3p0/

Related

StackOverflowError thrown when C3P0 jars are added

I'm writing simple app using Hibernate. I have hibernate.cfg.xml file as follows:
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/demo
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<!-- Use the C3P0 connection pool provider -->
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<!--List of XML mapping files -->
<mapping class="www.Message"/>
</session-factory>
</hibernate-configuration>
and retrieve SessionFactory object as follows:
//in HibernateUtil.class
SessionFactory sessionFactory;
Configuration configuration=new
Configuration().configure("hibernate.cfg.xml");
sessionFactory=configuration.buildSessionFactory();`
And everything works fine until I add C3P0 jars:
c3p0-0.9.5.2.jar
hibernate-c3p0-5.2.14.Final.jar
mchange-commons-java-0.2.11.jar
After adding jars above, the app throws StackOverflowError.
Here's my main App class:
package www;
import org.hibernate.Session;
import utils.HibernateUtil;
import java.util.List;
public class HelloWorld {
public static void main(String[] args) {
Session session = null;
try {
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(new Message("Hello Denmark!"));
session.getTransaction().commit();
session.close();
System.out.println("Message saved...");
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
List messages = session.createQuery("from Message m order by m.text
asc").list();
System.out.println("Messages found:"+messages.size());
for (Object mes: messages) {
Message message = (Message) mes;
System.out.println(message.getText());
}
session.getTransaction().commit();
session.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) session.close();
}
}
}
Any help would be appreciated.

How can i get jdbc connection from org.springframework.jdbc.datasource.DriverManagerDataSource class

I'm trying to get jdbc connection using the below code.
I use mysql database jpa2 and spring 4. How can I get jdbc connection and retrieve this value from mysql database
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
#ManagedBean
#ViewScoped
public class JDBCTest implements Serializable{
private JdbcTemplate jdbcTemplate;
void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final long serialVersionUID = 1L;
public void testDB(){
Connection con=null;
try {
con = getJdbcTemplate().getDataSource().getConnection();
PreparedStatement pst=con.prepareStatement("select * from global_class");
ResultSet st=pst.executeQuery();
while(st.next()){
System.out.println("Class Name :"+st.getString(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
When is run above this code then i get this exception
WARNING: #{jDBCTest.testDB}: java.lang.NullPointerException
javax.faces.FacesException: #{jDBCTest.testDB}: java.lang.NullPointerException
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
at org.springframework.faces.webflow.FlowActionListener.processAction(FlowActionListener.java:71)
at org.springframework.faces.model.SelectionTrackingActionListener.processAction(SelectionTrackingActionListener.java:64)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
Specify your database configuration in context file as below
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/db" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
and then start inject this datasource object into the JDBCTest class using spring dependency injection.
ex:
#autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
or
<bean id="jdbcTest" class="JDBCTest"><property name="dataSource" ref="datasource"/></bean>
Your bean is a Jsf Managed bean so your JdbcTemplate property should be annotated with #ManagedProperty else nothing will be injected.
Fist add a JdbcTemplate as a bean to the application context.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Then in your bean annotate your JdbcTemplate with #ManagedProperty and create a setJdbcTemplate method.
#ManagedProperty("#{jdbcTemplate}")
private JdbcTemplate jdbcTemplate;
The JdbcTemplate is designed to make working with JDBC easier. So use it. What you have constructed is a very complex way to do the same ugly thing. Use the JdbcTemplate correctly and for what it is intended to.
Your code should be something like this.
getJdbcTemplate().query("select * from global_class", new RowCallbackHandler() {
public void procesRow(ResultSet rs, int row) {
System.out.println("Class Name :" + rs.getString(1));
}
});
This does the same as your code, but uses the JdbcTemplate as it is intended.

Hibernate java.lang.OutOfMemoryError: Java heap space

I have a Java web application running on Tomcat 7 - jdk1.7
This application uses Spring 4.1.5.RELEASE and Hibernate 4.2.2.Final
My problem is a OutOfMemoryException of the Heap space on building section factory
This is my static method that opens a SessionFactory
public class GenericDAO {
public static SessionFactory sessionFactory = null;
public static ServiceRegistry serviceRegistry = null;
Transaction tx = null;
public static SessionFactory createSessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(
configuration.getProperties()). buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
}
And this is an example of DAO
public class SpecificDAO extends GenericDAO {
public int save(MyObject item) {
Session session = createSessionFactory().openSession();
try {
tx = session.beginTransaction();
session.save(item);
tx.commit();
return item.getId();
} catch (HibernateException e) {
if (tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return -1;
}
}
The error occurs at the line containing
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
The problem doesn't occur immediately at the deploy, but after 2 o 3 days of usage
This is my Hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName</property>
<property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<mapping class="it.company.client.project.hibernate.MyObject"/>
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>
You have to create the session factory only once as it is a heavy weight object, refer to the hibernate documentation for its details.
Here is the sample code from the doc on how it should be created:
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory(
new StandardServiceRegistryBuilder().build() );
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
It is better idea to flush and clear the session after used, you can use both
session.flush();
session.clear();
for more information link1 and link2
You are creating a SessionFactory object for every save() call.
i.e you are creating a new SessionFactory repeatedly for every save() call but not closing the existing SessionFactory objects in memory.
How many times save() is called ? the same no of SessionFactory will be in memory, which causes the memory leak.
SessionFactory are heavy weight objects, so you'd create at application initialization. You can create a SingleTon to instantiate SessionFactory.
Avoid instantiation of SessionFactory object on every DAO action. It is very slow and causes memory leaks. Better explained in this answer
If you're using Spring anyway, better to delegate to Spring work with SessionFactory, transactions and handling SQL exceptions. For example, your save() method will reduce to one line of code sessionFactory.getCurrentSession().save(item); Manual transaction open/commit/rollback should be replaced with #Transactional attribute. Also, usually better place transactions on whole service method, not on every single DAO method, but it depends of business logic.
Here example how to configure spring context for work with Hibernate (just first article for google)
I slightly adopted this example for current question
#Repository
public class SpecificDAO {
private SessionFactory sessionFactory;
#Autowired
public SpecificDAO(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Transactional(propagation=Propagation.REQUIRED)
public int save(MyObject item) {
try{
sessionFactory.getCurrentSession().save(item);
}catch (HibernateException e) {
return -1;
}
}
}
Spring configuration
<context:annotation-config/>
<context:component-scan base-package="it.company.client.project"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>it.company.client.project.hibernate.MyObject</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.connection.provider_class">org.hibernate.connection.DatasourceConnectionProvider</prop>
<prop key="hibernate.show_sql">false</prop>
<!--prop key="hibernate.hbm2ddl.auto">update</prop-->
</props>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

MyBatis - connection to MySQL randomly dropping

*UPDATE: I found a method which didn't close the session after openning. I think this might be the cause. Will test and report later. *
We are using MyBatis with our GWT Java web application. The problem is that sometimes an exception happens while trying to read or write to the database with MyBatis. What could be the cause? Any subsequent queries will work. It seems like the connection is timed out and needs to be refreshed. This happens sometimes through the day, we don't see any pattern in this. We tried different configurations to no avail.
org.apache.ibatis.exceptions.PersistenceException:
### Error opening session. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.
The MyBatis configuration file:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/project"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
<property name="poolMaximumActiveConnections" value="20"/>
<property name="poolMaximumIdleConnections" value="5"/>
<property name="poolPingEnabled" value="true"/>
<property name="poolPingQuery" value="select 1"/>
</dataSource>
</environment>
</environments>
Update 1
The exception is thrown in different "DAOs", it's not specific to a single method/call.
A general method might look like this:
#Override
public Entity get(String id) throws Exception {
LogHelper.logMethodStart(logger, "get", "id", id);
SqlSession session = null;
try {
session = GenericDao.SESSION_FACTORY.openSession();
EntityDao mapper = session.getMapper(EntityDao.class);
return mapper.get(id);
} catch (Exception e) {
logger.error(e);
throw e;
} finally {
if (session != null) {
session.close();
}
}
}
Where the session factory class consists of:
public static SqlSessionFactory SESSION_FACTORY;
static {
logger.info("SqlSessionFactory init started.");
String aResource = "iBatisConfig.xml";
Reader reader;
try {
reader = Resources.getResourceAsReader(aResource);
} catch (IOException e) {
throw new RuntimeException(e);
}
SESSION_FACTORY = new SqlSessionFactoryBuilder().build(reader);
try {
reader.close();
} catch (IOException e) {
logger.error(e);
}
SESSION_FACTORY.getConfiguration().addMappers("com.example.project.server.dao");
logger.info("SqlSessionFactory init end.");
}
Problem solved for me. I went though all the methods that were opening a session and one session was not being closed - coding error. Thus the connection pool ran out of free connection sometimes.

How can I set Datasource when I'm creating Hibernate SessionFactory?

I'm creating SessionFactory and I have my datasource as object in code where I'm creating SessionFactory, but i cannot set datasource to Hibernate Configuration object. So how can I set my datasource to my SessionFactory?
Configuration configuration = new Configuration();
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
configuration.setProperties(properties);
configuration.setProperty("packagesToScan", "com.my.app");
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
If you happen to have your DataSource stored in JNDI, then simply use:
configuration.setProperty(
"hibernate.connection.datasource",
"java:comp/env/jdbc/yourDataSource");
But if you use a custom data source provider like Apache DBCP or BoneCP and you don't want to use a dependency injection framework like Spring, then you may inject it on the StandardServiceRegistryBuilder before creating the SessionFactory:
//retrieve your DataSource
DataSource dataSource = ...;
Configuration configuration = new Configuration()
.configure();
//create the SessionFactory from configuration
SessionFactory sf = configuration
.buildSessionFactory(
new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties())
//here you apply the custom dataSource
.applySetting(Environment.DATASOURCE, dataSource)
.build());
Note that if you use this approach, you don't need to put the connection parameters in your hibernate.cfg.xml anymore. Here's an example of a compatible hibernate.cfg.xml file when using approach from above:
<?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.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="show_sql">false</property>
<!-- your mappings to classes go here -->
</session-factory>
</hibernate-configuration>
Code above tested on Hibernate 4.3.
To supply JDBC connections to Session, you need an implementation of ConnectionProvider.
By default, Hibernate uses DatasourceConnectionProvider which obtains a DataSource instance from JNDI.
To use a custom DataSource instance, use InjectedDataSourceConnectionProvider and inject the DataSource instance into it.
There is TODO note on InjectedDataSourceConnectionProvider
NOTE :
setDataSource(javax.sql.DataSource)
must be called prior to
configure(java.util.Properties).
TODO : could not find where
setDataSource is actually called.
Can't this just be passed in to
configure???
As per the note, call setDataSource() method from configure() method.
public class CustomConnectionProvider extends InjectedDataSourceConnectionProvider {
#Override
public void configure(Properties props) throws HibernateException {
org.apache.commons.dbcp.BasicDataSource dataSource = new BasicDataSource();
org.apache.commons.beanutils.BeanUtils.populate( dataSource, props );
setDataSource(dataSource);
super.configure(props);
}
}
You can also extend UserSuppliedConnectionProvider.
According to the contract of ConnectionProvider
Implementors should provide a public
default constructor.
Hibernate will invoke this constructor if custom ConnectionProvider is set through Configuration instance.
Configuration cfg = new Configuration();
Properties props = new Properties();
props.put( Environment.CONNECTION_PROVIDER, InjectedDataSourceConnectionProvider.class.getName() );
cfg.addProperties(props);
Luiggi Mendoza's answer is why my search sent me here, but I figure I should give my version because I spent quite some time looking around for how to do this - it sets it up with the Spring in-memory database for testing, a SessionContext and the hbm.xml in case you're not using annotations:
/**
* Instantiates a H2 embedded database and the Hibernate session.
*/
public abstract class HibernateTestBase {
private static EmbeddedDatabase dataSource;
private static SessionFactory sessionFactory;
private Session session;
#BeforeClass
public static void setupClass() {
dataSource = new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("file:SQLResources/schema-1.1.sql").
addScript("file:SQLResources/schema-1.2.sql").
build();
Configuration configuration = new Configuration();
configuration.addResource("hibernate-mappings/Cat.hbm.xml");
configuration.setProperty("hibernate.dialect",
"org.hibernate.dialect.Oracle10gDialect");
configuration.setProperty("hibernate.show_sql", "true");
configuration.setProperty("hibernate.current_session_context_class",
"org.hibernate.context.internal.ThreadLocalSessionContext");
StandardServiceRegistryBuilder serviceRegistryBuilder =
new StandardServiceRegistryBuilder();
serviceRegistryBuilder.applySetting(Environment.DATASOURCE, dataSource);
serviceRegistryBuilder.applySettings(configuration.getProperties());
StandardServiceRegistry serviceRegistry =
serviceRegistryBuilder.build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
sessionFactory.openSession();
}
#AfterClass
public static void tearDown() {
if (sessionFactory != null) {
sessionFactory.close();
}
if (dataSource != null) {
dataSource.shutdown();
}
}
#Before
public final void startTransaction() {
session = sessionFactory.getCurrentSession();
session.beginTransaction();
}
#After
public final void rollBack() {
session.flush();
Transaction transaction = session.getTransaction();
transaction.rollback();
}
public Session getSession() {
return session;
}
}
and you'll need these:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.184</version>
<scope>test</scope>
</dependency>
If your datasource is bounded at the JNDI tree:
configuration.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test");
Otherwise, if you have a DataSource object in code, which you want to use:
java.sql.Connection conn = datasource.getConnection();
Session session = sessionFactory.openSession(conn);
I would recommend the first one, to let Hibernate handle the connection lifecycle as needed. At the second approach, make sure that you close the connection when it's no longer needed.
I don't think you can. The Hibernate API will let you configure the JDBC properties so that it can manage the connections itself, or you can give it a JNDI DataSource location so it can go and fetch it, but I don't think you can give it a DataSource.
On the off-chance that you're using Spring, it's easier - use LocalSessionFactoryBean to configure Hibernate, and inject your DataSource into that. Spring performs the necessary magic in the background.
If you are using Spring framework, then use LocalSessionFactoryBean for injecting your data source to Hibernate SessionFactory.
<beans>
<bean id="YourClass"
class="com.YourClass.
<property name="sessionFactory">
<ref bean="DbSessionFactory" />
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>org.postgresql.Driver</value>
</property>
<property name="url">
<value>jdbc:postgresql://localhost/yourdb</value>
</property>
<property name="username">
<value>postgres</value>
</property>
<property name="password">
<value>postgres</value>
</property>
</bean>
<bean id="DbSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>conf/hibernate/UserMapping.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect"> org.hibernate.dialect.PostgreSQLDialect </prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.cache.use_second_level_cache"> true </prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
</props>
</property>
</bean>
</beans>
If you've implemented a class with javax.sql.DataSource, Hibernate's DataSource can be set by configuring properties.
import javax.sql.DataSource;
public class HibernateDataSource implements DataSource {
...
}
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
public class MyHibernateCfg {
public void initialize() {
HibernateDataSource myDataSource = new HibernateDataSource();
Configuration cfg = new Configuration();
// this is how to configure hibernate datasource
cfg.getProperties().put(Environment.DATASOURCE, myDataSource);
...
}
}
import org.hibernate.cfg.Configuration;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
public class TableClass {
public void initialize() {
MyHibernateCfg cfg = new MyHibernateCfg();
Configuration conf = cfg.getCfg();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
SessionFactory sessionFactory = conf.buildSessionFactory(serviceRegistry);
Session sessionFactory.openSession();
...
}
}
I used LocalContainerEntityManagerFactoryBean to create EntityManagerFactory instance at the configuration class.
If it is required to set another DataSource, than it is possible to update it with entity manager factory instance at runtime:
#Service("myService")
public class MyService
{
....
#Autowired
private LocalContainerEntityManagerFactoryBean emf;
....
public void replaceDataSource(DataSource dataSource)
{
emf.setDataSource(dataSource);
emf.afterPropertiesSet();
}
....
}
It works with Hibernate 5.2.9 Final.

Categories