Bonjour,

j'ai un service de création d'utilisateurs, dont je me sert pour tester la gestion des transactions avec Spring et JDBC en utilisant un TransactionTemplate. Pourtant, si je déclenche une exception en créant 2 fois le même utilisateur, et que je mets le status de la transaction à rollbackOnly, les utilisateurs créés précédemment dans la même transaction sont toujours là !

Voici un extrait du code de mon service :
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
 
    private TransactionTemplate txTemplate;
    private IUserDao userDao;
 
    public UserService(IUserDao dao, PlatformTransactionManager txManager) {
        this.userDao = dao;
        txTemplate = new TransactionTemplate(txManager);
    }
 
public User testTransaction( ) {
        txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
        User user = null;
        user = txTemplate.execute(new TransactionCallback<User>( ) {
 
            @Override
            public User doInTransaction(TransactionStatus status) {
                User user = null;
                try {
                    createUser("étienne", "789456");
                    createUser("marcel", "123456");
                    user = createUser("étienne", "789456");
                } catch (DataAccessException e) {
                    status.setRollbackOnly( );
                    if ( !status.isNewTransaction( ))
                        throw e;
                }
                return user;
            }
        });
 
        return user;
}
 
    public User createUser(final String login, final String password) {
 
        txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
        return txTemplate.execute(new TransactionCallback<User>( ) {
 
            @Override
            public User doInTransaction(TransactionStatus status) {
                String hashPassword = BCrypt.hashpw(password, BCrypt.gensalt( ));
                User user = null;
                try {
                    userDao.createUser(login, hashPassword);
                } catch (DataAccessException e) {
                    status.setRollbackOnly( );
                    if ( !status.isNewTransaction( ))
                        throw e;
                }
 
                return user;
            }
        });
}
Un extrait de UserDao :
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
 
    private NamedParameterJdbcTemplate template;
 
    public void setDatasource(DataSource datasource) {
        template = new NamedParameterJdbcTemplate(datasource);
    }
 
    public User createUser(String login, String hashPassword) {
 
        MapSqlParameterSource params = new MapSqlParameterSource( );
        params.addValue("login", login);
        params.addValue("hashPassword", hashPassword);
 
        template.update(createUserQuery, params);
 
        return new User(login, hashPassword);
    }
Voici mon fichier de configuration pour l'injection des beans :
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
 
    <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="xx"/>
        <property name="password" value="yy"/>
        <property name="url" value="jdbc:mysql://localhost/gesicoba"/>
        <property name="defaultAutoCommit" value="false"/>
    </bean>
 
 
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>
 
    <bean id="userDao" class="fr.atatorus.gesicoba.dao.UserDao" init-method="init">
        <property name="datasource" ref="datasource"/>
        <property name="queryLoader" ref="queryLoader"/>
    </bean>
 
    <bean id="userService" class="fr.atatorus.gesicoba.services.UserService">
        <constructor-arg ref="userDao"/>
        <constructor-arg ref="txManager"/>
    </bean>
Et enfin le code de mon test unitaire :
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
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( {
    "../test-application-context.xml"
})
public class UserServiceTest {
 
    @Resource
    private IUserService userService;
 
    @Test
    public void testTransaction( ) {
        User user = userService.testTransaction( );
        assertNull(user);
 
        user = userService.connectUser("étienne", "789456");
        assertNull(user);
 
    }
....
}
Puisque je crée 2 fois étienne, j'obtiens bien une exception, dans laquelle je mets bien le status de la transaction à rollbackOnly. Pourtant quand je regarde dans ma base, étienne existe bien, le rollback n'a pas eu lieu...

Un grand merci à ceux qui me donneront la source du mystère !!

Edit : j'ai fini par trouver. Le problème ne venait pas de mon code, mais de mySql. Je n'avais pas précisé à la création le moteur à utiliser, et ma table était de type MyIsam. En indiquant que je voulais un moteur InnoDB, ça fonctionne.