For logging and debugging purposes I want to dump an embedded/in-memory HSQL database to a file. Schema + Data. I'm using the spring-framework with hibernate.
I've tried both:
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.createSQLQuery("BACKUP DATABASE TO '/tmp/backup.tar.gz' BLOCKING");
transaction.commit();
session.close();
and
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.createSQLQuery("SCRIPT '/tmp/backup-data.sql'");
transaction.commit();
session.close();
Both to no avail.
There is no special configuration.
Hibernate config:
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:spring/batch/database/schema.sql"/>
</jdbc:embedded-database>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="packagesToScan" value="com.domain.*.model"/>
<property name="hibernateProperties">
<value>
hibernate.format_sql=true
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
What did I miss, do I need a different approach, is it even possible?
Your code is only creating an instance of a SQLQuery and throws it away. You aren't executing the query.
Add/change to the following
SQLQuery query = session.createSQLQuery("BACKUP DATABASE TO '/tmp/backup.tar.gz' BLOCKING");
query.executeUpdate();
This will execute the query.
Have you tried:
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.createSQLQuery("BACKUP DATABASE TO '/tmp/' BLOCKING");
transaction.commit();
session.close();
According to the documentation you need to give the directory and HSQL will create the tar.gz file in it.
Related
I just have a new project to maintain, and using Hibernate+Spring. I wrote a DeliveryInfoServiceImpl which have a method to query for some entity but not have any update or save operation, but I have an error:
Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Unless I add #Transactional on the method or the class.
My questions are :
Why I have to add #Transactional though I am only executing select query?
Does adding #Transactional means enable transaction support ,which may have more unnecessary overhead when I am only using "select" query.
Below is my code snippet:
#Override
public List<UnavailableRestaurantBean> getUnavailableRestaurantBean(String custAddress, List<Long> dishesId) {
List<Dish> dishes = getDishByIds(dishesId);//exception here
....
}
private List<Dish> getDishByIds(List<Long> ids){
return deliveryInfoDao.findByIds(Dish.class,ids);
}
And I have below transaction manager config:
<tx:annotation-driven transaction-manager="myTxManager" />
<bean id="myTxManager" name="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory">
</property>
</bean>
I'm using Hibernate 4.3.10 with Java 8.
To be more explicit see the following code example :
public static long createBlindStructure(BlindStructure pBlindStructure){
Transaction tcx = null;
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
int id = -1;
try{
tcx = session.beginTransaction();
id = session.save(pBlindStructure);
tcx.commit();
}
catch( Throwable e){
tcx.rollback();
}
finally{
session.close();
}
return id;
}
In my mind this method save() open session and transaction, save my object and close session and transaction. From save() I tried to get back the identifier as describe in javadoc. But It doesn't work, I see the request execute in my log (thanks to Hibernate debug mode).
Hibernate: insert into PokerLeagueManager.blindStructure (structureJson) values (?)
But when I try this :
public static long createBlindStructure(BlindStructure pBlindStructure){
Transaction tcx = null;
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
try{
tcx = session.beginTransaction();
session.save(pBlindStructure);
tcx.commit();
}
catch( Throwable e){
tcx.rollback();
}
finally{
session.close();
}
return pBlindStructure.getIdBlindStructure();
}
It correctly saved my object.
I test one more case :
Just returning a constant and don't put Id in the variable like first example and It work. It seems that object is not save in case I get the ID directly with session.save() method.
Moreover, I observe something interesting. I made a first test with one of the solution which worked, it generated a database data with Id 117. Then I changed my code for the solution which difsn't work and reloaded it in Tomcat , I made 2nd try without success. I changed again my code for the one which succeded and the id was generated is 120. It missed 2nf id number (the 2nd try I've done ??)
To help you see my hibernate.cfg.xml file
<?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 settings -->
<property name='connection.driver_class'>com.mysql.jdbc.Driver</property>
<property name='connection.url'>jdbc:mysql://XXXXXX:XXXX/PokerLeagueManager</property>
<property name='connection.username'>XXXXX</property>
<property name='connection.password'>XXXXXX</property>
<property name="show_sql">true</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">120</property>
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.acquireRetryAttempts">1</property>
<property name="hibernate.c3p0.acquireRetryDelay">250</property>
<!-- Dev -->
<property name="hibernate.c3p0.validate">true</property>
<!-- SQL dialect -->
<property name='dialect'>org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name='show_sql'>true</property>
<mapping resource="mappings/BlindStructure.hbm.xml"/>
<mapping resource="mappings/Tournament.hbm.xml"/>
<mapping resource="mappings/LegalFee.hbm.xml"/>
</session-factory>
Edit: User3813463 answer for a part of a question. It explains that session are in AUTO flush mode by default which flush session in some case like :
flush occurs by default at the following points:
before some query executions
from org.hibernate.Transaction.commit()
from Session.flush()
But I my point of view (I maybe miss understand something), my first case should worked because I commit my transaction.
Secondly, I need advise to choose a flush mode. To my mind Commit mode is a good way to do for method which make only inserting or only reading data on database.
Do you agree with me, have you some sources where this is debate?
This all is the magic of FlushMode read docs here
According to docs default FlushMode is AUTO that means
The Session is sometimes flushed before query execution in order to
ensure that queries never return stale state.
According to another document (Here):
flush occurs by default at the following points:
before some query executions
from org.hibernate.Transaction.commit()
from Session.flush()
So when you say pBlindStructure.getIdBlindStructure(); hibernate actually perform flush on current session, resulting data is getting saved in DB.
The session save method returns an object for the generated id by the generator. This value you should cast to Long.
long id = -1;
try{
tcx = session.beginTransaction();
id = (Long) session.save(pBlindStructure);
tcx.commit();
}
catch( Throwable e){
tcx.rollback();
}
finally{
session.close();
}
return id;
I am using Spring and Hibernate in my application and using Spring Transaction.
So I have a service layer with annotation #Transaction on methods and DAO layer having methods for database query.
#Transactional(readOnly = false)
public void get(){
}
The issue is when I want to save an object in the database,then I have to use session.flush() at the end of DAO layer method. Why?
I think if I have annotated #Transaction, then Spring should automatically commit the transaction on completion of the service method.
DAO layer :
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
try {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(entity);
session.flush();
} catch (HibernateException he) {
throw new Exception("Failed to save entity " + entity);
}
return entity;
}
Service layer :
#Transactional(readOnly = false)
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
return dao.saveEntity(entity);
}
spring config :
<context:property-placeholder properties-ref="deployProperties" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Activate Spring Data JPA repository support -->
<jpa:repositories base-package="com" />
<!-- Declare a datasource that has pooling capabilities-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
<!-- Declare a JPA entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
p:persistenceUnitName="hibernatePersistenceUnit"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="hibernateVendor"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource" p:configLocation="${hibernate.config}"
p:packagesToScan="com" />
<!-- Specify our ORM vendor -->
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"/>
<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
Yes, if you have #Transactional for your DAO method then you need not flush the session manually, hibernate will take care of flushing the session as part of committing the transaction if the operations in the method are successful.
Check this link to know on how #Transactional works - Spring - #Transactional - What happens in background?
By default, hibernate stacks its queries so they can be optimized when they are finally executed onto the database.
The whole point of flush is to flush this stack and execute it in your transaction onto the database. Your leaving the "safe" house of the JVM and execute your query on a big strange database.
This is why you can't select something you've just saved without a flush. It's simply not in the database yet.
The meaning of commit is to end the transaction and make changes of the database visible for others. Once commit has been executed there's no return possible anymore.
Frankly I'm not exactly sure if it is a best practice but for normal CRUD operations you should be able to add flush into your DAO layer.
This way you don't need to worry about it into the service layer.
If you want java to optimize your transaction then you'll have to add it into your service layer. But remember that you don't need to solve performance issues when there aren't any! Flushes all over your code into the service layer is not good for the code readability. Keep it simple and stupid ;)
My application is based on Hibernate 3.2 and Spring 2.5. Here is the transaction management related snippet from the application context:
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
<bean id="transactionTemplate" classs="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:/hibernate.cfg.xml"></property>
</bean>
For all the DAO's there are relevant Service class and the transactions are handled there using #Transactional on each method in the service layer. However there is a scenario now that a method in DAO say "parse()" is called from the service layer. In the service layer I specified #Transactional(readOnly=false). This parse method in the DAO calls another method say "save()" in the same DAO which stores a large number of rows (around 5000) in the database. Now the save method is called in a loop from the parse function. Now the issue is that after around 100 calls to the "save" method.. i sometimes get a OutOfMemory Exception or sometimes the program stops responding.
For now these are the changes which I have made to the save method:
Session session = getHibernateTemplate().getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
int counter = 0;
if(books!=null && !books.isEmpty()){
for (Iterator iterator = books.iterator(); iterator
.hasNext();) {
Book book = (Book) iterator.next();
session.save(book);
counter++;
if(counter % 20==0) {
session.flush();
session.clear();
}
}
}
tx.commit();
session.close();
This is the only method in my application where I start a transaction like this and commit it at the end of method. Otherwise I normally just call getHibernateTemplate.save(). I am not sure whether I should perform transaction management for this save method separately in the DAO by placing #Transactional(readOnly=false, PROPOGATION=NEW) on save(), or is this approach okay?
Also I have updated the hibernate.jdbc.batch_size to 20 in the hibernate.cfg configuration file.
Any suggestions?
For the batch insertion with hibernate, the best practice is StatelessSession, it doesn`t cache any states of your entity, you will not encounter OutOfMemory, the code like:
if (books == null || books.isEmpty) {
return;
}
StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession();
Transaction tx = session.beginTransaction();
for (Book each : books) {
session.insert(book);
}
tx.commit();
session.close();
And the Transaction of StatelessSession is independent from the current transaction context.
You only need the bit with flushing and clearing the session. Leave transaction management to Spring. Use sessionFactory.getCurrentSession() to reach the session that Spring has already opened for you. Also, Spring's recent recommmendation is to avoid HibernateTemplate and work directly with Hibernate's API. Inject SessionFactory to your dao-bean.
I would refactor parse in a way it doesn't call save directly but takes some callback from service layer. Service layer would pass its transactional method with save call as this callback.
It may not work exactly as decribed in your case but from this short description this would be something I'd try.
I'm new to Hibernate.
Automatically created hibernate.cfg.xml (Netbeans wizard)
Automatically created HibernateUtil.java
Automatically created POJO class with annotations
Trying to get object from database but getting error:
Exception in thread "pool-1-thread-1" org.hibernate.HibernateException: get is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
getting an object:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
CallInfo ci = (CallInfo) session.get(CallInfo.class, ucid);
hibernate.cfg.xml
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/sochi_feedback</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property>
<property name="hibernate.current_session_context_class">thread</property>
Add
Transaction tx = session.beginTransaction(); //This statement will initiate the transaction
just before your CallInfo ci = (CallInfo) session.get(CallInfo.class, ucid);
and at the end of your transaction commit the changes by calling..
tx.commit();
Another solution is to use openSession() instead of getCurrentSession(). Then transactions can be used only when required for updating queries.
Session session = HibernateUtil.getSessionFactory().openSession();
CallInfo ci = (CallInfo) session.get(CallInfo.class, ucid);
Even after beginTransaction() and and commit() if you still get the
Caused by: org.hibernate.HibernateException: setDefaultReadOnly is not valid without active transaction
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352)
go to "Start" and search for services and restart the database service
Before you actually start the transaction you need to start the session by calling session.beginTransaction(), right after you create sessionFactory.