UncategorizedSQLException: Hibernate flushing
Désolé pour le titre aussi peu expressif, mais je ne savais pas trop comment appeler ce post ^^
Bref, voila ma situation: je suis en train de tester Spring/hibernate dans une petite appli et j'ai un problème au moment de faire les tests JUnits de mes DAO.
la conf de Spring
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost/dog"/>
<property name="username" value="postgres"/>
<property name="password" value="******"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>./fr/dog/bean/Race.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.use_outer_join">true</prop>
</props>
</property>
</bean>
<!-- transaction interceptor -->
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- autoproxy -->
<bean id="transactionBeanNameProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames"><value>*DAO</value></property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<bean id="raceDAO" class="fr.dog.dao.RaceDAO">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
</beans> |
Mon DAO:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class RaceDAO implements IRaceDAO {
private SessionFactory sessionFactory;
/**
* @return the sessionFactory
*/
public SessionFactory getSessionFactory()
{
return sessionFactory;
}
/**
* @param sessionFactory the sessionFactory to set
*/
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
// ....
@Override
public void update(Race race)
{
sessionFactory.getCurrentSession().update(race);
}
} |
ma classe de Test:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class TestRace extends TestCase{
private IRaceDAO dao;
private AbstractApplicationContext factory;
public void setUp()
{
factory = new ClassPathXmlApplicationContext("spring-hibernate.xml");
dao = (IRaceDAO)factory.getBean("raceDAO");
SessionTransactionManager.start((SessionFactory)factory.getBean("sessionFactory"));
}
public void tearDown()
{
SessionTransactionManager.stop((SessionFactory)factory.getBean("sessionFactory"));
}
public void testUpdate()
{
Race a = new Race("Elfe");
Long id = dao.save(a);
a.setNom("Night Elf");
dao.update(a);
//((SessionFactory)factory.getBean("sessionFactory")).getCurrentSession().flush();
Race b = dao.get(id);
assertEquals("Night Elf", b.getNom());
} |
J'ai lu ailleurs qu'en fait, comme je test hors du container, je dois gérer mes sessions moi même donc j'ai repiqué un code qui à l'air de fonctionner pour les autres tests:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class SessionTransactionManager
{
public static void start(SessionFactory sessionFactory)
{
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
public static void stop (SessionFactory sessionFactory)
{
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.releaseSession(sessionHolder.getSession(), sessionFactory);
}
} |
Et donc mon problème, c'est que quand je passe mon test de update, j'ai cette exception:
Citation:
Envoyé par la console
org.springframework.jdbc.UncategorizedSQLException: Hibernate flushing: Could not execute JDBC batch update; uncategorized SQLException for SQL [update race set nom=? where id=?]; SQL state [null]; error code [0]; L'élément du batch 0 update race set nom=Night Elf where id=252 a été annulé. Appeler getNextException pour en connaître la cause.; nested exception is java.sql.BatchUpdateException: L'élément du batch 0 update race set nom=Night Elf where id=252 a été annulé. Appeler getNextException pour en connaître la cause.
Citation:
Envoyé par le logger
1478 [main] ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: transaction is read-only
1478 [main] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
Et je dois dire que je ne suis pas sûr de comprendre pourquoi... Si je décommente la ligne qui fait le flush, tout se passe bien mais pour quelle raison? Est-ce que ca a un rapport avec la conf de Spring qui met PROPAGATION_REQUIRED,readOnly sur les methodes get* ?
Merci d'avance :mrgreen:
Gerer le flush de la session explicitement
Bonjour,
Tu as raison, il faut dans la méthode setup(), (réalisant l'isolation de ta méthode de test) gérer explicitement le flush de la session, afin de réaliser la synchronisation avec la base. Pour cela, avant de réaliser le binding de la transaction, essaies de faire un session.setFlushMode(FlushMode.AUTO).
Voila,
bon w-e.