Bonjour,

Je voudrais mettre un lock sur une table durant la transaction. C-à-d très exactement faire une "LOCK TABLE personne WRITE;"

Comme on peut le lire dans le code ci-dessous, je n'accepte de faire un "update" que si la valeur du champ [VERSION] est identique à la valeur que j'ai lu avant de faire la mise à jour.

Extrait:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
    //modifier une personne
    protected void updatePersonne(Personne personne) {
        [...]
        //modification
        int n = getSqlMapClientTemplate().update("Personne.updateOne", personne);
 
        if (n==0) throw new DaoException("La personne d'Id ["+personne.getId()+" n'existe pas ou bien a été modifié", 2);
    }
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
 
[...]
<sqlMap>
[...]
    <!-- mettre à jour une personne -->
    <update id="Personne.updateOne" parameterClass="Personne.classe">
        UPDATE
        PERSONNES
        SET
        VERSION=#version#+1,
        NOM=#nom#,
        PRENOM=#prenom#,
        DATENAISSANCE=#dateNaissance#,
        MARIE=#marie#,
        NBENFANTS=#nbEnfants#
        WHERE ID=#id#
          AND VERSION=#version#
    </update>
[...]
</sqlMap>
Ce que je veux faire, c'est "locker" la table personne avant ma transaction pour m'assurer que personne ne pourra modifier la valeur du champ [VERSION] durant ma transaction et enfin à la fin de la transaction libérer le verrou par un "UNLOCK TABLES".

Comment puis-je faire cela?

table [personne]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
 
ID	VERSION	NOM	PRENOM	DATENAISSANCE	MARIE	NBENFANTS
424	3	DUPONT	Alain	1975-11-03	0	2
443	1	DURANT	Nathalie1984-12-07	0	2
445	2	DUVAL	Giuseppe1991-02-05	1	0
CODE SOURCE COMPLET:
Classe personne:
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
 
package istia.st.springmvc.personnes.entites;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class Personne 
{
    //identifiant unique de la personne
    private int id;
 
    //la version actuelle
    private long version;
 
    //le nom
    private String nom;
 
    //le prénom
    private String prenom;
 
    //la date de naissance
    private Date dateNaissance;
 
    //l'état marital
    private boolean marie = false;
 
    //le nombre d'enfants
    private int nbEnfants;
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public long getVersion() {
        return version;
    }
 
    public void setVersion(long version) {
        this.version = version;
    }
 
    public String getNom() {
        return nom;
    }
 
    public void setNom(String nom) {
        this.nom = nom;
    }
 
    public String getPrenom() {
        return prenom;
    }
 
    public void setPrenom(String prenom) {
        this.prenom = prenom;
    }
 
    public Date getDateNaissance() {
        return dateNaissance;
    }
 
    public void setDateNaissance(Date dateNaissance) {
        this.dateNaissance = dateNaissance;
    }
 
    public boolean isMarie() {
        return marie;
    }
 
    public void setMarie(boolean marie) {
        this.marie = marie;
    }
 
    public int getNbEnfants() {
        return nbEnfants;
    }
 
    public void setNbEnfants(int nbEnfants) {
        this.nbEnfants = nbEnfants;
    }
 
    //constructeur par défaut
    public Personne() {
    }
 
    //constructeur avec initialisation des champs de la personne
    public Personne(int id, String nom, String prenom, Date dateNaissance, int nbEnfants, boolean marie) {
        this.id = id;
        this.marie = marie;
        this.nom = nom;
        this.prenom = prenom;
        this.dateNaissance = dateNaissance;
        this.nbEnfants = nbEnfants;
    }
 
    public Personne(int id, String nom, String prenom, Date dateNaissance, boolean marie, int nbEnfants ) {
        this.id = id;
        this.marie = marie;
        this.nom = nom;
        this.prenom = prenom;
        this.dateNaissance = dateNaissance;
        this.nbEnfants = nbEnfants;
    }
 
    //constructeur d'une perosnne par recopie d'une autre personne
    public Personne(Personne p)
    {
        setId(p.getId());
        setVersion(p.getVersion());
        setNom(p.getNom());
        setPrenom(p.getPrenom());
        setDateNaissance(p.getDateNaissance());
        setMarie(p.isMarie());
        setNbEnfants(p.getNbEnfants());
    }
 
    //toString
    public String toString() 
    {
        return "[" +
               id + ", " +
               version + ", " +
               prenom + ", " +
               nom + ", " +
               new SimpleDateFormat("dd/MM/yyyy").format(dateNaissance) + ", " +
               marie + ", " +
               nbEnfants +
               "]"; 
    }
 
 
}
Pour accèder à la table, les classes et interfaces:
IDAO interface:
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
 
package istia.st.springmvc.personnes.dao;
 
import istia.st.springmvc.personnes.entites.Personne;
 
import java.util.Collection;
 
public interface IDao 
{
    //liste de toutes les personnes
    Collection getAll();
 
    //obtenir une personne particulière
    Personne getOne(int id);
 
    //ajouter/modifier une personne
    void saveOne(Personne personne);
 
    //supprimer une personne
    void deleteOne(int id);
}
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
 
package istia.st.springmvc.personnes.dao;
 
import istia.st.springmvc.personnes.entites.Personne;
import java.util.ArrayList;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
 
import java.util.Collection;
import java.util.List;
 
public class DaoImplCommon extends SqlMapClientDaoSupport implements IDao
{
 
    //liste des personnes
    @Override
    public Collection getAll() {
        return getSqlMapClientTemplate().queryForList("Personne.getAll", null);
    }
 
    //obtenir une personne en particulier
    @Override
    public Personne getOne(int id) {
        Personne personne;
        // on la récupère dans le BD        
        List tmpP = (List) getSqlMapClientTemplate().queryForList("Personne.getOne", new Integer(id));
 
        if (tmpP.isEmpty())
            personne=null;
        else
        {
            personne = (Personne) tmpP.get(0);
        }
 
        //Personne personne = (Personne) getSqlMapClientTemplate().queryForList("Personne.getOne", new Integer(id));
        // a-t-on récupéré qq chose?
        if (personne==null)
        {
            //on lance une exception 
            throw new DaoException("La personne d'id [" + id + "] n'existe pas", 2);
        }
 
        return personne;
    }
 
    @Override
    public void saveOne(Personne personne) {
        // le paramètre personne est-il valide ?
        check(personne);
 
        //ajout ou modification ?
        if (personne.getId()==-1)
            insertPersonne(personne);
        else updatePersonne(personne);
 
        //on attend 5 sec - pour les tests mettre true au lieu de false
        System.out.println("attend 5 sec...");
        if (true) wait(5000);        
    }
 
    //suppression d'une personne
    @Override
    public void deleteOne(int id) {
        //on supprime la personne
        int n = getSqlMapClientTemplate().delete("Personne.deleteOne", new Integer(id));
 
        // a-t-on réussi
        if (n==0)
            throw new DaoException("Personne d'id ["+id+"] inconnue", 2);
    }
 
    //ajouter une personne
    protected void insertPersonne(Personne personne) {
        //1ère version
        personne.setVersion(1);
 
        //on attend 10 ms - pour les tests mettre true au lieu de false
        if (true) wait(10);
 
        //on insère la nouvelle perosnne dans la table de la BD
        getSqlMapClientTemplate().insert("Personne.insertOne", personne);        
    }
 
    //modifier une personne
    protected void updatePersonne(Personne personne) {
        //on attend 10 ms - pour les tests mettre true au lieu de false
        if (true) wait(10);
        /*
        try 
        {
            synchronized(this){ wait(10); }
        }
        catch(InterruptedException e) { }
        */
        //modification
        int n = getSqlMapClientTemplate().update("Personne.updateOne", personne);
 
        if (n==0) throw new DaoException("La personne d'Id ["+personne.getId()+" n'existe pas ou bien a été modifié", 2);
    }
 
    //vérifier validité d'une personn
    private void check(Personne p) {
        // personne p
        if (p == null) {
                throw new DaoException("Personne null", 10);
        }
        // id
        if (p.getId() != -1 && p.getId() < 0) {
                throw new DaoException("Id [" + p.getId() + "] invalide", 11);
        }
        // date de naissance
        if (p.getDateNaissance() == null) {
                throw new DaoException("Date de naissance manquante", 12);
        }
        // nombre d'enfants
        if (p.getNbEnfants() < 0) {
                throw new DaoException("Nombre d'enfants [" + p.getNbEnfants()
                                + "] invalide", 13);
        }
        // nom
        if (p.getNom() == null || p.getNom().trim().length() == 0) {
                throw new DaoException("Nom manquant", 14);
        }
        // prénom
        if (p.getPrenom() == null || p.getPrenom().trim().length() == 0) {
                throw new DaoException("Prénom manquant", 15);
        }
    }
 
    // attente
    private void wait(int N) {
            // on attend N ms
            try {
                    Thread.sleep(N);
            } catch (InterruptedException e) {
                    // on affiche la trace de l'exception
                    e.printStackTrace();
                    return;
            }
    }
 
}
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
 
package istia.st.springmvc.personnes.dao;
 
public class DaoException extends RuntimeException
{
    private int code;
 
    public int getCode() {
        return code;
    }
 
    public DaoException(String message, int code) {
        super(message);
        this.code = code;
    }
 
}
Les classes et interfaces pour configurer la transaction:
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
 
package istia.st.springmvc.personnes.service;
 
import istia.st.springmvc.personnes.entites.Personne;
 
import java.util.Collection;
 
public interface IService {
	// liste de toutes les personnes
	Collection getAll();
 
	// obtenir une personne particuli�re
	Personne getOne(int id);
 
	// ajouter/modifier une personne
	void saveOne(Personne personne);
 
	// supprimer une personne
	void deleteOne(int id);
 
	// ajouter plusieurs personnes
	void saveMany(Personne[] personnes);
 
	// supprimer plusieurs personnes
	void deleteMany(int ids[]);
}
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
 
package istia.st.springmvc.personnes.service;
 
import istia.st.springmvc.personnes.entites.Personne;
import istia.st.springmvc.personnes.dao.IDao;
 
import java.util.Collection;
 
public class ServiceImpl implements IService {
 
	// la couche [dao]
	private IDao dao;
 
	public IDao getDao() {
		return dao;
	}
 
	public void setDao(IDao dao) {
		this.dao = dao;
	}
 
	// liste des personnes
	public Collection getAll() {
		return dao.getAll();
	}
 
	// obtenir une personne en particulier
	public Personne getOne(int id) {
		return dao.getOne(id);
	}
 
	// ajouter ou modifier une personne
	public void saveOne(Personne personne) {
		dao.saveOne(personne);
	}
 
	// suppression d'une personne
	public void deleteOne(int id) {
		dao.deleteOne(id);
	}
 
	// sauvegarder une collection de personnes
	public void saveMany(Personne[] personnes) {
		// on boucle sur le tableau des personnes
		for (int i = 0; i < personnes.length; i++) {
			dao.saveOne(personnes[i]);
		}
	}
 
	// supprimer une collection de personnes
	public void deleteMany(int[] ids) {
		// ids : les id des personnes à supprimer
		for (int i = 0; i < ids.length; i++) {
			dao.deleteOne(ids[i]);
		}
	}
}
Les fichiers xml pour les 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
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
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- la source de données DBCP -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://192.168.1.199:3306/test</value>
        </property>
        <property name="username">
            <value>[LOGIN]</value>
        </property>
        <property name="password">
            <value>[MOT DE PASSE]</value>
        </property>
    </bean>
 
    <!-- SqlMapClient -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="dataSource">
            <ref local="dataSource" />
        </property>        
        <property name="configLocation">
            <value>classpath:sql-map-config-mysql.xml</value>
        </property>
    </bean>
 
    <!-- la classe d'accès à la couche [dao] -->
    <bean id="dao" class="istia.st.springmvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient" />
        </property>
    </bean>
 
    <!-- gestionnaire de transactions -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="dataSource" />
        </property>
    </bean>
 
    <!-- la classe d'accès à la couche [service] -->
    <bean id="service" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref local="transactionManager" />
        </property>
        <property name="target">
            <bean class="istia.st.springmvc.personnes.service.ServiceImpl">
                <property name="dao">
                    <ref local="dao" />
                </property>
            </bean>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
</beans>
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
 
<?xml version="1.0" encoding="UTF-8"?>
<!-- <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" 
"http://www.ibatis.com/dtd/sql-map-config-2.dtd"> -->
 
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL MAP Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
    <sqlMap resource="personne-mysql.xml" />        
</sqlMapConfig>
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
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
	PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
	"http://ibatis.apache.org/dtd/sql-map-2.dtd">
 
<sqlMap>
    <!-- alias classe [Personne] -->
    <typeAlias alias="Personne.classe" type="istia.st.springmvc.personnes.entites.Personne" />
 
    <!-- mapping table [PERSONNES] - objet [Personne] -->
    <resultMap id="Personne.map" class="istia.st.springmvc.personnes.entites.Personne">
        <result property="id" column="ID" />
        <result property="version" column="VERSION" />
        <result property="nom" column="NOM" />
        <result property="prenom" column="PRENOM" />
        <result property="dateNaissance" column="DATENAISSANCE" />
        <result property="marie" column="MARIE" />
        <result property="nbEnfants" column="NBENFANTS" />
    </resultMap>
 
    <!-- LISTE DE TOUTES LES PERSONNES -->
    <select id="Personne.getAll" resultMap="Personne.map">
        SELECT
        ID,
        VERSION,
        NOM,
        PRENOM,
        DATENAISSANCE,
        MARIE,
        NBENFANTS
        FROM
        PERSONNES
    </select>
 
    <!-- obtenir une personne en particulier -->
    <select id="Personne.getOne" resultMap="Personne.map">
        SELECT
        ID,
        VERSION,
        NOM,
        PRENOM,
        DATENAISSANCE,
        MARIE,
        NBENFANTS
        FROM
        PERSONNES
        WHERE ID=#value#
    </select>
 
    <!-- ajouter une personne -->
    <insert id="Personne.insertOne" parameterClass="Personne.classe">
        INSERT INTO
        PERSONNES
        (VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS)
        VALUES
        (#version#, #nom#, #prenom#, #dateNaissance#, #marie#, #nbEnfants#)
        <selectKey keyProperty="id">
            select LAST_INSERT_ID() as value
        </selectKey>    
    </insert>
 
    <!-- mettre à jour une personne -->
    <update id="Personne.updateOne" parameterClass="Personne.classe">
        UPDATE
        PERSONNES
        SET
        VERSION=#version#+1,
        NOM=#nom#,
        PRENOM=#prenom#,
        DATENAISSANCE=#dateNaissance#,
        MARIE=#marie#,
        NBENFANTS=#nbEnfants#
        WHERE ID=#id#
          AND VERSION=#version#
    </update>
 
    <!-- supprimer une perosnne -->
    <delete id="Personne.deleteOne" parameterClass="int">
        DELETE
        FROM 
        PERSONNES
        WHERE ID=#value#
    </delete>
 
    <!-- obtenir la valeur de la clé primaire [id] de la dernière personne insérée -->
    <select id="Personne.getNextId" resultClass="int">
        SELECT LAST_INSERT_ID()
    </select>
</sqlMap>
Et enfin le prog. principal:
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
 
package istia.st.springmvc.personnes.tests;
 
import istia.st.springmvc.personnes.dao.IDao;
import istia.st.springmvc.personnes.entites.Personne;
 
import istia.st.springmvc.personnes.service.IService;
import java.util.Collection;
import java.util.Iterator;
 
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;
 
public class MainTestServiceDao {
	public static void main(String[] args) {
                IService service= (IService) new XmlBeanFactory(new ClassPathResource("spring-config-dao-mysql.xml")).getBean("service");
 
                Personne pers = service.getOne(424);
                pers.setNbEnfants(15);
                service.saveOne(pers);               
	}
}