Bonjour,
J'ai un soucis de transaction. J'essaie de faire un insert d'un objet sur un champ unique, et si le champ unique existe déjà je voudrais retenter l'insert avec un incrément. C'est le moyen le plus rapide je pense (vu la quantité d'insert que je dois faire et vu qu'il y a très peu de cas où il faudra retenter l'insert).
Donc pour cela lors de mon merge de mon DAO j'ai catché l'erreur par EntityExistException que je remonte par une Exception perso à mon service. Cela fonctionne bien et me permet de retenter l'insert avec l'incrément. Mais le problème est qu'un rollback est exécuté quand même et une exception de transaction est lancée. Voici l'erreur :
et les LOG pour retracer l'opération :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 Exception in thread "main" org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:465) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:709) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:678) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy63.findValidLogin(Unknown Source) at org.esco.sarapis.db.domain.gestion.impl.InsertManagerImpl.main(InsertManagerImpl.java:92) Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:51) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:456) ... 8 more
voici mes différents codes :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 13391 DEBUG [main] AbstractBeanFactory - Returning cached instance of singleton bean 'loginAliasManagerImpl' 13406 DEBUG [main] AbstractBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0' 13406 DEBUG [main] AbstractPlatformTransactionManager - Using transaction object [org.springframework.orm.jpa.JpaTransactionManager$JpaTransactionObject@14fa79d] 13406 DEBUG [main] AbstractPlatformTransactionManager - Creating new transaction with name [org.esco.sarapis.db.domain.gestion.ILoginAliasManager.findValidLogin]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 13422 DEBUG [main] JpaTransactionManager - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@eb840f] for JPA transaction 13485 DEBUG [main] SessionImpl - opened session at timestamp: 12174115935 13485 DEBUG [main] JDBCTransaction - begin 13485 DEBUG [main] ConnectionManager - opening JDBC connection 13485 DEBUG [main] DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/JSarapis] 13500 DEBUG [main] JDBCTransaction - current autocommit status: true 13500 DEBUG [main] JDBCTransaction - disabling autocommit 13500 DEBUG [main] JDBCContext - after transaction begin 13516 DEBUG [main] JpaTransactionManager - Exposing JPA transaction as JDBC transaction [SimpleConnectionHandle: com.mysql.jdbc.Connection@1b80d9b] 13516 DEBUG [main] TransactionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@856d3b] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1c5ddc9] to thread [main] 13531 DEBUG [main] TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder@3e1d25] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@1eb717e] to thread [main] 13531 DEBUG [main] TransactionSynchronizationManager - Initializing transaction synchronization 13531 DEBUG [main] TransactionAspectSupport - Getting transaction for [org.esco.sarapis.db.domain.gestion.ILoginAliasManager.findValidLogin] 13531 INFO [main] LoginAliasManagerImpl - Create new Login : aaaaa 13531 INFO [main] LoginAliasManagerImpl - Try to insert Login 13531 DEBUG [main] GenericDaoImpl - Merging org.esco.sarapis.db.entity.personne.Login instance 13547 DEBUG [main] TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@3e1d25] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@1eb717e] bound to thread [main] 13547 DEBUG [main] IdentifierValue - id unsaved-value: 0 13547 DEBUG [main] AbstractSaveEventListener - transient instance of: org.esco.sarapis.db.entity.personne.Login 13547 DEBUG [main] DefaultMergeEventListener - merging transient instance 13563 DEBUG [main] AbstractSaveEventListener - saving [org.esco.sarapis.db.entity.personne.Login#<null>] 13563 DEBUG [main] AbstractSaveEventListener - executing insertions 13563 DEBUG [main] AbstractSaveEventListener - executing identity-insert immediately 13563 DEBUG [main] AbstractEntityPersister - Inserting entity: org.esco.sarapis.db.entity.personne.Login (native id) 13578 DEBUG [main] AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 13578 DEBUG [main] AbstractBatcher - insert into Login (nom) values (?) Hibernate: insert into Login (nom) values (?) 13578 DEBUG [main] AbstractBatcher - preparing statement 13594 DEBUG [main] AbstractEntityPersister - Dehydrating entity: [org.esco.sarapis.db.entity.personne.Login#<null>] 13594 DEBUG [main] NullableType - binding 'aaaaa' to parameter: 1 13594 DEBUG [main] AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 13594 DEBUG [main] AbstractBatcher - closing statement 13594 DEBUG [main] JDBCExceptionReporter - could not insert: [org.esco.sarapis.db.entity.personne.Login] [insert into Login (nom) values (?)] com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry 'aaaaa' for key 2 ... 13594 WARN [main] JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000 13594 ERROR [main] JDBCExceptionReporter - Duplicate entry 'aaaaa' for key 2 13594 DEBUG [main] AbstractEntityManagerImpl - mark transaction for rollback 13594 ERROR [main] GenericDaoImpl - Merge failed - Entity already exists : 13594 DEBUG [main] GenericDaoImpl - ====>>> duplicate entry. 13594 DEBUG [main] LoginAliasManagerImpl - Interception de l'erreur : null 13594 INFO [main] LoginAliasManagerImpl - Create new Login : aaaaa0 13594 INFO [main] LoginAliasManagerImpl - Try to insert Login 13594 DEBUG [main] GenericDaoImpl - Merging org.esco.sarapis.db.entity.personne.Login instance 13594 DEBUG [main] TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@3e1d25] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@1eb717e] bound to thread [main] 13594 DEBUG [main] IdentifierValue - id unsaved-value: 0 13594 DEBUG [main] AbstractSaveEventListener - transient instance of: org.esco.sarapis.db.entity.personne.Login 13594 DEBUG [main] DefaultMergeEventListener - merging transient instance 13610 DEBUG [main] AbstractSaveEventListener - saving [org.esco.sarapis.db.entity.personne.Login#<null>] 13610 DEBUG [main] AbstractSaveEventListener - executing insertions 13610 DEBUG [main] AbstractSaveEventListener - executing identity-insert immediately 13610 DEBUG [main] AbstractEntityPersister - Inserting entity: org.esco.sarapis.db.entity.personne.Login (native id) 13610 DEBUG [main] AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 13610 DEBUG [main] AbstractBatcher - insert into Login (nom) values (?) Hibernate: insert into Login (nom) values (?) 13610 DEBUG [main] AbstractBatcher - preparing statement 13610 DEBUG [main] AbstractEntityPersister - Dehydrating entity: [org.esco.sarapis.db.entity.personne.Login#<null>] 13610 DEBUG [main] NullableType - binding 'aaaaa0' to parameter: 1 13610 DEBUG [main] IdentifierGeneratorFactory - Natively generated identity: 13 13610 DEBUG [main] AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 13610 DEBUG [main] AbstractBatcher - closing statement 13610 DEBUG [main] GenericDaoImpl - Merge successful. 13610 INFO [main] LoginAliasManagerImpl - insert OK 13610 DEBUG [main] TransactionAspectSupport - Completing transaction for [org.esco.sarapis.db.domain.gestion.ILoginAliasManager.findValidLogin] 13610 DEBUG [main] AbstractPlatformTransactionManager - Triggering beforeCommit synchronization 13610 DEBUG [main] AbstractPlatformTransactionManager - Triggering beforeCompletion synchronization 13610 DEBUG [main] AbstractPlatformTransactionManager - Initiating transaction commit 13610 DEBUG [main] JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@eb840f] 13610 DEBUG [main] JDBCTransaction - rollback 13688 DEBUG [main] JDBCTransaction - re-enabling autocommit 13688 DEBUG [main] JDBCTransaction - rolled back JDBC Connection 13688 DEBUG [main] JDBCContext - after transaction completion 13688 DEBUG [main] ConnectionManager - aggressively releasing JDBC connection 13688 DEBUG [main] ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 13688 DEBUG [main] SessionImpl - after transaction completion 13688 DEBUG [main] AbstractPlatformTransactionManager - Triggering afterCompletion synchronization 13688 DEBUG [main] TransactionSynchronizationManager - Clearing transaction synchronization 13688 DEBUG [main] TransactionSynchronizationManager - Removed value [org.springframework.orm.jpa.EntityManagerHolder@3e1d25] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@1eb717e] from thread [main] 13688 DEBUG [main] TransactionSynchronizationManager - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@856d3b] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1c5ddc9] from thread [main] 13688 DEBUG [main] JpaTransactionManager - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@eb840f] after transaction 13688 DEBUG [main] EntityManagerFactoryUtils - Closing JPA EntityManager 13703 DEBUG [main] SessionImpl - closing session 13703 DEBUG [main] ConnectionManager - connection already null in cleanup : no action
Le dao générique :
le dao étendu :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 public abstract class GenericDaoImpl<T> implements IGenericDao<T> { /** Logger. */ static final Logger LOGGER = Logger.getLogger(GenericDaoImpl.class); /** Type de l'entity. */ private Class<T> entityBeanType; /** Le persistence contexte injecté par Spring. */ @PersistenceContext(type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; // Constructeurs /** * Constructeur de l'objet GenericJpaDao.java. * @param classe Type de l'entity actuel. */ public GenericDaoImpl(final Class<T> classe) { this.entityBeanType = classe; } public T merge(final T entity) throws DuplicateEntryException { LOGGER.debug("Merging " + this.entityBeanType.getName() + " instance"); T res = null; try { res = entityManager.merge(entity); LOGGER.debug("Merge successful."); } catch (EntityExistsException e) { LOGGER.error("Merge failed - Entity already exists : " /*+ e.getMessage()*/); // if (e.getMessage().contains("Duplicate entry")) { LOGGER.debug("====>>> duplicate entry."); throw new DuplicateEntryException(/*e.getMessage()*/); // } // LOGGER.debug("====<> duplicate entry passe à côté."); //throw e; } catch (NestableRuntimeException e) { // TODO: handle exception LOGGER.error("Merge de " + getEntityBeanType().getName() + " Impossible. " + e.getMessage(), e); throw new DatabaseException("Merge impossible", e); } return res; }
et le service :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 @Repository public class LoginDaoImpl extends GenericDaoImpl<Login> implements ILoginDao { /** * Constructeur de l'objet LoginDaoImpl.java. */ public LoginDaoImpl() { super(Login.class); // TODO Auto-generated constructor stub } }
et ma config Spring :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 @Service @Scope("singleton") public class LoginAliasManagerImpl implements ILoginAliasManager { private static final Logger LOGGER = Logger.getLogger(LoginAliasManagerImpl.class); @Resource private ILoginDao loginDao; public LoginAliasManagerImpl() { // TODO Auto-generated constructor stub } @Transactional private Login findLogin(final String possibleValue, final int i) { Login login = null; try { if (i == -1) { LOGGER.info("Create new Login : " + possibleValue); login = new Login(possibleValue); } else { LOGGER.info("Create new Login : " + possibleValue + i); login = new Login(possibleValue + i); } LOGGER.info("Try to insert Login"); login = loginDao.merge(login); LOGGER.info("insert OK"); } catch (DuplicateEntryException e) { // TODO Auto-generated catch block // e.printStackTrace(); LOGGER.debug("Interception de l'erreur : " + e.getMessage()); return null; } return login; } @Transactional public Login findValidLogin(final String possibleValue) { Login login = null; int i = -1; while (login == null) { login = findLogin(possibleValue, i); i++; } return login; } }
Quelqu'un aurait une idée de comment résoudre cela ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 <?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- Propriétés à chager --> <bean name="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:db.properties</value> </property> </bean> <!-- Source de donnéees DB --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="${db.driver}" p:url="${db.url}" p:username="${db.username}" p:password="${db.password}" /> <!-- Configuration de l'unité de persistence --> <bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"> <!-- On spécifie ici les lieux où trouver les fichiers de persistence --> <property name="persistenceXmlLocations"> <list> <value>classpath*:META-INF/persistence.xml</value> </list> </property> <!-- On spécifie ici les sources de données à utiliser, locale ou distante --> <property name="dataSources"> <map> <entry key="localDataSource" value-ref="dataSource" /> <!--<entry key="remoteDataSource" value-ref="remote-db" />--> </map> </property> <!-- On spécifie ici la sources de données par défaut si aucune source de données n'est disponible --> <property name="defaultDataSource" ref="dataSource" /> </bean> <!-- Configuration du manager d'entity --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:persistenceUnitManager-ref="persistenceUnitManager"> <!-- On spécifie ici l'adaptateur Spring pour l''implémentation JPA utilisée --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" p:databasePlatform="${db.dialect}" p:database="${db.database}" p:showSql="true" p:generate-ddl="true" /> </property> <!-- On spécifie ici le tisseur utilisée pour la modification du ByteCode, cf documentation de Spring pour plus de précisions --> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> </property> <!-- On spécifie ici le dialecte utilisé en fonction de l'implémentation JPA utilisée --> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> </property> </bean> <!-- le gestionnaire de transactions --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" scope="prototype"> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> </property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!--Activation de la prise en compte des annotations de type @Required,@Autowired,@PostConstruct, @PreDestroy,@Resource,@PersistenceContext,@PersistenceUnit --> <context:annotation-config /> <context:component-scan base-package="org.esco.sarapis.db" /> <!-- traduction des exceptions --> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> <!-- persistence --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> </beans>
Sinon j'envisageai de recréer une transaction lors d'un Duplicate entry, mais le problème est que je ne vois pas comment je pourrais réinitialiser la transaction.
Partager