Spring #Transactional commiting partial results even exception is thrown - java

I am running an Apache CXF web service under spring. I use JPA to persist the information. The service has a method that updates a series of rows. Before persisting each row, I check that the values to be persisted really exist in the databes. If there is a value that does not exists, then an Exception is thrown. The problem is I need to rollback al the values updated. I though that using #Transactional in my web service method would do the trick, but instead of that, the values that got persisted are really modified in the database, wich is not the desired behavior.
This is the code of the web service method
#Transactional( propagation = Propagation.REQUIRED )
public UpdateDescriptionResponse updateDescription(UpdateDescriptionRequest updateDescriptionRequest) throws SIASFaultMessage {
try {
SubstanceEntity substance = service.findSubstanceBySubstanceID(updateDescriptionRequest.getUpdateDescriptionRequestData().getIdentity().getSubstanceID());
if (substance!=null){
for(DescriptionKeyValueType keyValue: updateDescriptionRequest.getUpdateDescriptionRequestData().getSubstanceDescriptionData() ){
boolean descriptionExists = false;
for(DescriptionEntity desc: substance.getDescriptionsById()){
if (desc.getDescKey().equals(keyValue.getKey())) {
descriptionExists = true;
break;
}
}
if (!descriptionExists){
SIASFaultDetail faultDetail = new SIASFaultDetail();
faultDetail.setSIASFaultDescription("Description key does not match given substance ID");
faultDetail.setSIASFaultMessage(SIASFaultCode.INVALID_INPUT.toString());
faultDetail.setSIASFaultType(SIASFaultCode.INVALID_INPUT);
SIASFaultMessage fault = new SIASFaultMessage("Description key does not match given substance ID", faultDetail);
throw fault;
}
else
descriptionLogic.updateDescription(substance.getSubstanceId(), keyValue.getKey(),keyValue.getValue());
}
UpdateDescriptionResponse response = new UpdateDescriptionResponse();
UpdateDescriptionResponse.UpdateDescriptionResponsePackage responsePackage = new UpdateDescriptionResponse.UpdateDescriptionResponsePackage();
ResponseStatus status = new ResponseStatus();
status.setMessage(messageOk);
status.setReturn(BigInteger.valueOf(0));
responsePackage.setResponseStatus(status);
response.setUpdateDescriptionResponsePackage(responsePackage);
return response;
}
else
{
SIASFaultDetail faultDetail = new SIASFaultDetail();
faultDetail.setSIASFaultDescription("Substance ID does not exists");
faultDetail.setSIASFaultMessage(SIASFaultCode.INVALID_SUBSTANCE_ID.toString());
faultDetail.setSIASFaultType(SIASFaultCode.INVALID_SUBSTANCE_ID);
SIASFaultMessage fault = new SIASFaultMessage("Substance ID does not exists", faultDetail);
throw fault;
}
} catch (SIASFaultMessage ex) {
throw ex;
} catch (Exception ex) {
SIASFaultDetail a = new SIASFaultDetail();
a.setSIASFaultDescription("Unknown error processing enroll request");
a.setSIASFaultMessage("SERVICE_ERROR");
a.setSIASFaultType(SIASFaultCode.UNKNOWN_ERROR);
SIASFaultMessage fault = new SIASFaultMessage("Something happened", a);
throw fault;
}
}
This is the code for the instance of descriptionLogic.updateDescription(...)
#Override
public void updateDescription(String substanceID, String key, String value) {
PageRequest page = new PageRequest(1, 1);
Map<String, Object> filters = new HashMap<String, Object>();
filters.put("SUBSTANCE_ID", substanceID);
List<SubstanceEntity> substances = substanceService.findAll(page, filters);
if (substances.size() == 0) {
return;
}
SubstanceEntity substanceEntity = substances.get(0);
for (DescriptionEntity desc : substanceEntity.getDescriptionsById()) {
if (desc.getDescKey().equals(key)) {
desc.setDescValue(value);
descriptionService.persist(desc);
}
}
}
This is the test that fails
#Test()
public void testUpdateDescription_does_not_modify_description_with_invalid_values() throws Exception {
UpdateDescriptionRequest request = new UpdateDescriptionRequest();
UpdateDescriptionRequest.UpdateDescriptionRequestData data = new UpdateDescriptionRequest.UpdateDescriptionRequestData();
SIASIdentity identity = new SIASIdentity();
identity.setSubstanceID("804ab00f-d5e9-40ff-a4d3-11c51c2e7479");
data.getSubstanceDescriptionData().add(new DescriptionKeyValueType() {{
setKey("KEY3_1");
setValue("NEW_VALUE_1");
}});
data.getSubstanceDescriptionData().add(new DescriptionKeyValueType() {{
setKey("KEY3_5");
setValue("NEW_VALUE_2");
}});
data.setIdentity(identity);
request.setUpdateDescriptionRequestData(data);
try {
siasService.updateDescription(request);
}
catch (SIASFaultMessage ex){
}
DescriptionEntity descriptionEntity1 = descriptionService.findById(1);
DescriptionEntity descriptionEntity2 = descriptionService.findById(2);
assertThat("The value does not math",descriptionEntity1.getDescValue(), not(equalTo("NEW_VALUE_1")));
assertThat("The value does not math",descriptionEntity2.getDescValue(), not(equalTo("NEW_VALUE_2")));
Assert.assertEquals("The description does not math","KEY3_1", descriptionEntity1.getDescKey());
Assert.assertEquals("The description does not math","KEY3_2", descriptionEntity2.getDescKey());
}
It fails in this line:
assertThat("The value does not math",descriptionEntity1.getDescValue(), not(equalTo("NEW_VALUE_1")));
This is my datasource configuration in my spring context configuration file
.
.
.
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<jdbc:initialize-database data-source="myDataSource">
<jdbc:script location="classpath:test-data.sql" />
</jdbc:initialize-database>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="packagesToScan" value="cu.jpa"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
<property name="jpaDialect">
<bean class="cu.jpa.specifications.IsolationSupportHibernateJpaDialect" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">${hdm2ddl.auto}</prop>
</props>
</property>
<property value="/META-INF/persistence.xml" name="persistenceXmlLocation"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>
.
.
.
This is my persistence.xml file content:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="NewPersistenceUnit">
<class>cu.jpa.entities.PatternEntity</class>
.
.
.
<class>cu.jpa.entities.TraceRegEntity</class>
</persistence-unit>
</persistence>
Extract of the test class:
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/repositories.xml"})
public class ServiceImplUpdateDescriptionTest {
.
.
.
#Test()
public void testUpdateDescription_does_not_modify_description_with_invalid_values() throws Exception{
.
.
.
}
}

Spring will only rollback the transaction if it is an unchecked exception, if the exception is a checked exception then you will have to add that to your #Transactional annotation.
#Transactional(rollbackFor = SIASFaultMessage.class)

Related

NullPointerException when selecting DataSource in MultiTenant Connection Provider

I repurposed an existing hibernate-spring project and upgraded to Hibernate 4 and Spring 4 and added multiple datasources using multitenancy. The application starts fine, the datasources are being read in using the MultiTenantDataSourceLookup class. When setting the new tenant, the tenant is resolved but then I get the nullpointerexception on line 41 of MultiTenantConnectionProviderImpl(See comment for line). I'm also using GenericHibernateDAO if that helps. I can post that code by request. I'm new to spring so the problem may be a very simple one. However, if more code is needed to help me, I will be happy to share more as I troubleshoot and research the problem myself. Any help will be greatly appreciated. Thanks. Here is the full stack trace: http://pastebin.com/LjyhTwvY
MultiTenantConnectionProviderImpl.java
public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl
{
#Autowired
private DataSource defaultDataSource;
#Autowired
private DataSourceLookup dataSourceLookup;
/**
* Select datasources in situations where not tenantId is used (e.g. startup processing).
*/
#Override
protected DataSource selectAnyDataSource() {
return defaultDataSource;
}
/**
* Obtains a DataSource based on tenantId
*/
#Override
protected DataSource selectDataSource(String tenantIdentifier) {
//Below is line 41 where the nullpointerexeption is occurring
DataSource ds = dataSourceLookup.getDataSource(tenantIdentifier);
return ds;
}
}
CurrentTenantIdentifierResolverImpl.java
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
private static final String KEY_TENANTID_SESSION = "hibernate.tenant_identifier_resolver";
private static final String DEFAULT_TENANTID = "customer1";
public String resolveCurrentTenantIdentifier() {
String tenant = resolveTenantByHttpSession();
System.out.println("Tenant resolved: " + tenant);
return tenant;
}
/**
* Get tenantId in the session attribute KEY_TENANTID_SESSION
* #return TenantId on KEY_TENANTID_SESSION
*/
public String resolveTenantByHttpSession()
{
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//If session attribute exists returns tenantId saved on the session
if(attr != null){
HttpSession session = attr.getRequest().getSession(false); // true == allow create
if(session != null){
String tenant = (String) session.getAttribute(KEY_TENANTID_SESSION);
if(tenant != null){
return tenant;
}
}
}
//otherwise return default tenant
return DEFAULT_TENANTID;
}
public boolean validateExistingCurrentSessions() {
return true;
}
}
Context.xml
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<bean id="multitenancyConnectionProvider"
class="com.github.elizabetht.util.MultiTenantConnectionProviderImpl"/>
<bean id="dataSourceLookup"
class="com.github.elizabetht.util.MultiTenantDataSourceLookup"/>
<bean id="tenantResolver"
class="com.github.elizabetht.util.CurrentTenantIdentifierResolverImpl"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>com.github.elizabetht.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
<prop key="hibernate.jdbc.lob.non_contextual_creation">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.multiTenancy">DATABASE</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.github.elizabetht.util.MultiTenantConnectionProviderImpl</prop>
<prop key="hibernate.tenant_identifier_resolver">com.github.elizabetht.util.CurrentTenantIdentifierResolverImpl</prop>
</props>
</property>
</bean>
<bean id="defaultDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/studentEnrollment" />
<property name="username" value="springy" />
<property name="password" value="pass" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="autodetectDataSource" value="false"/>
</bean>
OUTPUT
Tenant resolved: customer1
Feb 25, 2017 1:34:31 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [studentHibernateServlet] in context with path [/StudentEnrollmentWithSpring] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at com.github.elizabetht.util.MultiTenantConnectionProviderImpl.selectDataSource(MultiTenantConnectionProviderImpl.java:41)
at org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl.getConnection(AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java:52)
at org.hibernate.internal.AbstractSessionImpl$ContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:423)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228)
I changed my hibernate properties to the following and everything works now:
<property name="hibernateProperties">
<map>
<entry key="hibernate.multi_tenant_connection_provider" value-ref="multitenancyConnectionProvider"/>
<entry key="hibernate.tenant_identifier_resolver" value-ref="tenantResolver"/>
<entry key="hibernate.multiTenancy" value="DATABASE"/>
</map>
</property>

Cannot set custom CurrentSessionContext class in Spring Session Factory

I am trying to implement a Session-Per-Conversation pattern in a JSF2-Spring-Hibernate Web App so I need my AnnotationSessionFactoryBean to build a Hibernate SessionFactory with a custom CurrentSessionContext class.
I have been receiving this error log:
org.springframework.dao.DataAccessResourceFailureException: Could not obtain Hibernate-managed Session for Spring-managed transaction; nested exception is org.hibernate.HibernateException: No CurrentSessionContext configured!
Here is my xml config for the whole data context used.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${datasource.driverClassName}" />
<property name="url" value="${datasource.url}" />
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.password}" />
<property name="initialSize" value="${datasource.poolInitialSize}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${org.hibernate.dialect.dialectmysqlInno}</prop>
<prop key="hibernate.hbm2ddl.auto">${org.hibernate.ddl.mode}</prop>
<prop key="hibernate.search.default.directory_provider">${org.hibernate.search.directoryprovidr}</prop>
<prop key="hibernate.current_session_context_class">
mx.gob.jgtjo.apps.schedule.web.conversation.ConversationalCurrentSessionContext
</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="dataSource" ref="dataSource" />
<property name="hibernateManagedSession" value="true" />
</bean>
<tx:annotation-driven order="0" transaction-manager="transactionManager" />
<context:component-scan base-package="mx.gob.jgtjo.apps.schedule.dao.hibernate" />
</beans>
Also, here is my hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="jgtjoSessionFactory">
<!--Entity -->
<mapping class="mx.gob.jgtjo.apps.schedule.model.AudienciaOral" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.CausaPenal" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.DefensorPenal" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.Delito" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.EventoAudiencia" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.Juez" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.MinisterioPublico" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.ParteMaterial" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.Sala" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.TipoAudiencia" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.User" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.Rol" />
<mapping class="mx.gob.jgtjo.apps.schedule.model.DelitoConfigurado" />
</session-factory>
</hibernate-configuration>
As you can see, nothing tricky with hibernate xml.
Why I keep getting this exception?
java.lang.NoSuchMethodException: mx.gob.jgtjo.apps.schedule.web.conversation.ConversationalCurrentSessionContext.<init>(org.hibernate.engine.SessionFactoryImplementor)
It seems that hibernate looks for a constructor in my class that has a SessionFactory as argument.
This is the code from Hibernate which attempts to build the current session context using whatever value you passed in using the hibernate.current_session_context_class property:
private CurrentSessionContext buildCurrentSessionContext() {
String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
// for backward-compatability
if ( impl == null && transactionManager != null ) {
impl = "jta";
}
if ( impl == null ) {
return null;
}
else if ( "jta".equals( impl ) ) {
if ( settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions() ) {
log.warn( "JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()" );
}
return new JTASessionContext( this );
}
else if ( "thread".equals( impl ) ) {
return new ThreadLocalSessionContext( this );
}
else if ( "managed".equals( impl ) ) {
return new ManagedSessionContext( this );
}
else {
try {
Class implClass = ReflectHelper.classForName( impl );
return ( CurrentSessionContext ) implClass
.getConstructor( new Class[] { SessionFactoryImplementor.class } )
.newInstance( new Object[] { this } );
}
catch( Throwable t ) {
log.error( "Unable to construct current session context [" + impl + "]", t );
return null;
}
}
}
As you can see acceptable values are jta, thread, managed.
Since you're using Spring's transaction management functionality you shouldn't set this property at all. Spring will take care of this for you.
You just need to annotate your transactional methods with #Transactional and a session will be opened and bound to the current thread for you.
Ok, as the anwer above showed me the code and the logging I showed in my question edit the implementation of the CurrentSessionContext interface must have a public constructor with a sessionFactory as argument.
Hibernate docs never say anything like that.
and here is my class:
package mx.gob.jgtjo.apps.schedule.web.conversation;
import javax.servlet.http.HttpServletRequest;
import mx.gob.jgtjo.apps.schedule.web.utils.JsfUtils;
import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.context.CurrentSessionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConversationalCurrentSessionContext implements CurrentSessionContext {
private static final long serialVersionUID = 803157986557235023L;
protected static final Logger log = LoggerFactory
.getLogger(ConversationalCurrentSessionContext.class);
public ConversationalCurrentSessionContext() {
}
#Override
public Session currentSession() throws HibernateException {
HttpServletRequest request = null;
try {
request = JsfUtils.getCurrentHttpRequest();
} catch (Exception e) {
log.debug("No current http request in faces context, returning default conversation.");
}
if (request == null) {
return (Session) ConversationManager.getDefaultConversationSession();
} else {
return (Session) ConversationManager.getSessionForRequest(request);
}
}
}
As you can see, I lack that constructor.

JDBC Datasource returning NULL

I have this working java code that serve as the "datasource":
public final class PMF {
static Driver driver = null;
static String url = "jdbc:jiql://local";
static Properties props = new Properties();
static {
String password = "jiql";
String user = "admin";
props.put("user",user);
props.put("password",password);
try {
Class clazz = Class.forName("org.jiql.jdbc.Driver");
driver = (Driver) clazz.newInstance();
} catch (Exception e){
e.printStackTrace();
}
}
public static Connection get() {
try{
return driver.connect(url,props);
} catch (java.sql.SQLException e){
e.printStackTrace();
}
return null;
}
}
When I tried to adapt this code for Spring with the code below:
jdbc.properties
jdbc.driverClassName=org.jiql.jdbc.Driver
# development
jdbc.url=jdbc:jiql://local
jdbc.username=admin
jdbc.password=jiql
applicationContext.xml
<!-- placeholders -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="/WEB-INF/jdbc.properties"/>
</bean>
<!-- data source -->
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
The "datasource" gets null when I do this in the DAO:
#Autowired
private DataSource dataSource;
What could be causing the datasource to be null?
It looks like it was a DAO issue and not JDBC issue. I re-created the app using Spring STS and everything went working.

Read/write to database from quartz jobs - transactions not working

I have two Quartz (1.8.3) jobs, configured via Spring (2.5.6), one of them writes (send) to database, and one reads from it (check).
<bean id="scheduleFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="Check"/>
<ref bean="Send"/>
</list>
</property>
</bean>
<bean id="Send" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="StatusMonitor" />
<property name="targetMethod" value="sendMessage" />
</bean>
</property>
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<bean id="Check" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="StatusMonitor" />
<property name="targetMethod" value="checkAndUpdateStatus" />
</bean>
</property>
<property name="cronExpression" value="30 0/1 * * * ?" />
</bean>
Transaction manager is set up:
<tx:annotation-driven transaction-manager="TransactionManager"/>
In both jobs I explicitly run read/write operations in transactions like this:
#Override
public synchronized void sendMessage() {
try {
TransactionTemplate tt = new TransactionTemplate(ptm);
tt.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
...
statusDAO.update(status);
...
}
});
log.info("Status was updated");
} catch (Exception e) {
...
}
}
where ptm is a TransactionManager bean, injected via Spring.
I see "Status was updated" record in logs, but when I read this record from transactional read method it is outdated sometimes. Moreover, when I use an SQL editor to read this record it is outdated too.
I don't understand, why transactions dont work in this case, do you have any ideas?
Thanks.
For anyone that might be interested. This worked for me
<bean name="applicationDataCollectorControllerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="org.mypckage.controller.jobs.ApplicationDataCollectorController" />
<property name="jobDataAsMap">
<map>
<!--<entry key="timeout" value="1" />-->
<entry key="genericService" value-ref="genericService" />
<entry key="applicationDataCollectorService" value-ref="applicationDataCollectorService" />
<entry key="transactionManager" value-ref="transactionManager" />
</map>
</property>
</bean>
--- in the scheduler bean---
#Override
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
getApplicationDataCollectorService().collectData(transactionManager);
}
----In the applicationDataCollectorService bean-----
public void collectData( org.springframework.transaction.jta.JtaTransactionManager transactionManager) {
try {
this.transactionManager = transactionManager;
testTransactionalSave();
} catch (Exception e) {
BUSY = false;
e.printStackTrace();
}
}
}
private void testTransactionalSave() throws Exception {
TransactionTemplate tt = new TransactionTemplate(transactionManager);
tt.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus ts) {
try {
ApplicationParameter appPara = null;
List<ApplicationParameter> appParaList = genericService.getListFromHQL("select o from ApplicationParameter as o order by o.id desc", false);
if (appParaList != null) {
if (appParaList.size() > 0) {
appPara = (ApplicationParameter) appParaList.get(0);
appPara.setLastBankStatementMailNum(appPara.getLastBankStatementMailNum() + 10);
appPara = (ApplicationParameter) genericService.mergeObject(appPara);
System.out.println(" num is now = " + appPara.getLastBankStatementMailNum());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
}
Note: dont forget to declare transactionManager as private properties in both beans with public setter and getter for spring to wire it up. Any Questions? yemiosigbesan#gmail.com

hibernate always deletes all data from a table on its own

Hi i am developing a spring mvc app thats using hibernate to connect to a mysql database that stores files.
I have two methods. one that adds all files from a specific file path of my choosing and another method that invokes a query to return me a list of the files stored from mysql.
The issue is this. When i execute the first method on its own ie populating the database, it works fine i can see the contents of that table from mysql command line. however, when i then execute the query method right after populating it, the contents of that said table is completely gone instantly. Its as if hibernate only stored the data in the mysql temporarily or somewhere in mysql, it deleted data imediatly and doesnt keep it their.
this is the method that populated the table:
/**
* Test Method: ideal for another class to do this kind of work and this
* pass the FileObject into this class
*/
public void addSomeFiles() {
System.out.println("addSomeFiles");
File dir = new File(picturesPath);
String[] fileNames = dir.list();
for (int i = 0; i < fileNames.length; i++) {
System.out.println(fileNames[i]);
File file = new File(picturesPath + "\\" + fileNames[i]);
if (file.isFile()) {
FileObject fileO = contstructFileObject(file);
if (fileO == null) {
System.out.println("fileO is null!!!!!");
} else {
// addFile(fileO);
dbFileHelper.addFile(fileO);
}
}
}
System.out.println("//////////////");
// File file;
}
.........Hibernate template class........
public class DbFileHelper implements DbFileWrapper {
private HibernateTemplate hbTemplate;
//private static final String SQL_GET_FILE_LIST = "select filename, size, id, type from fileobject";
private static final String SQL_GET_FILE_LIST = "select new FileObject(filename, size, id, type) from FileObject";
public DbFileHelper() {
}
public void setHbTemplate(HibernateTemplate hbTemplate) {
System.out.println("setHbTemplate");
System.out.println("///////////////////");
System.out.println("///////////////////");
System.out.println("///////////////////");
this.hbTemplate = hbTemplate;
}
// ////////////////////////////////////////////////
#Override
public String addFile(FileObject file) {
// TODO Auto-generated method stub
System.out.println("addFile using hibernate");
if (hbTemplate == null) {
System.out.println("hbTemplate is null!! why?");
}
hbTemplate.saveOrUpdate(file);
hbTemplate.flush();
return "added succesfuly";
}
And here is the other method that makes the query:
........................
public JSONArray getFileList(String type){
return constructJsonArray(dbFileHelper.getFileList(ALL));
}
private JSONArray constructJsonArray(List<FileObject> fileList ){
JSONArray mJsonArray = new JSONArray();
for (int i = 0; i < fileList.size(); i++) {
System.out.println("fileName = " + fileList.get(i).getFilename() );
//mJson.put("Filename", fileList.get(i).getFileName() );
mJsonArray.add( new JSONObject().put("File ID", fileList.get(i).getId() ));
mJsonArray.add( new JSONObject().put("Filename", fileList.get(i).getFilename() ));
mJsonArray.add( new JSONObject().put("File type", fileList.get(i).getType()));
mJsonArray.add( new JSONObject().put("File Size", fileList.get(i).getSize()));
}
return mJsonArray;
}
..........hibernate Template class.......
private static final String SQL_GET_FILE_LIST = "select new FileObject(filename, size, id, type) from FileObject";
#Override
public List<FileObject> getFileList(String type) {
// TODO Auto-generated method stub
List<FileObject> files = hbTemplate.find(SQL_GET_FILE_LIST);
//hbTemplate.flush();
return files;
}
..........
Finally here is a print screen of what i originaly put inside my table but dissapears on its own:
http://img411.imageshack.us/img411/9553/filelisti.jpg
Am i missing something here?
edit: additional info.
my hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.kc.models.FileObject" >
<class name="com.kc.models.FileObject" table="fileobject">
<id name="id" column="ID">
<generator class="native" />
</id>
<property name="filename" type="string" column="FILENAME" />
<property name="type" type="string" column="TYPE" />
<property name="size" type="double" column="SIZE" />
<property name="file" type="blob" length="1000000000" column="FILE" />
</class>
</hibernate-mapping>
my controller:
#Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// TODO call a method that returns a list of Mobile Apps.
testAddingSomeFilesToDb();
return new ModelAndView("" + "testJsonResponse", "jsonArray",
getFileList() );
}
private void testAddingSomeFilesToDb() {
ctx = new ClassPathXmlApplicationContext("zang-file-service.xml");
FileHelper file = (FileHelper) ctx.getBean("fileHelper");
file.addSomeFiles();
}
/**
* Get file list from sql server based on type
* #return file list in json
*/
private JSONArray getFileList() {
// TODO: Get request parameter that states what type of file extensions
// the client wants to recieve
ctx = new ClassPathXmlApplicationContext("zang-file-service.xml");
FileHelper file = (FileHelper) ctx.getBean("fileHelper");
return file.getFileList("all");
}
Another edit:
my .xml file configuring the session factory and hibernate template
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
<!-- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd -->
<!-- Config properties files -->
<!-- Hibernate database stuff -->
<!-- <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"> <list> <value>/properties/jdbc.properties</value>
</list> </property> </bean> -->
<!-- <bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driver}" /> <property
name="url" value="${database.url}" /> <property name="username" value="${database.user}"
/> <property name="password" value="${database.password}" /> </bean> -->
<bean id="dataSource1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/zangshop" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<!-- LocalSessionFactoryBean u need to put the hbm files in the WEB-INF/classes
root director -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource1"></property>
<property name="mappingResources">
<list>
<value>FileObject.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<bean id="hbTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="dbFileHelper" class="com.kc.models.DbFileHelper">
<property name="hbTemplate" ref="hbTemplate"></property>
</bean>
<bean id="fileHelper" class="com.kc.models.FileHelper">
<property name="dbFileHelper" ref="dbFileHelper"></property>
</bean>
</beans>
i have fixed the problem
i changed <prop key="hibernate.hbm2ddl.auto">create</prop>
to <prop key="hibernate.hbm2ddl.auto">update</prop> and it worked
Are you creating/destroying the SessionFactory between calls? Could you have the hbm2ddl.auto property set to create-drop?
Actually, can you show the Hibernate settings?
Reference
Hibernate Core Reference Guide
Table 3.7. Miscellaneous Properties
In my case also table was getting deleted automatically, following solution worked for me:
org.hibernate.dialect.MySQL8Dialect
Appending the version number with the MySQL Dialect.
Because commit was not getting executed earlier with org.hibernate.dialect.MySQLDialect.

Categories