Bonjour à tous.

Je suis actuellement sur un très gros projet, qui sera utilisé évidemment par plusieurs personnes à un même instant, et qui bien sûr travailleront souvent sur des objets communs (comme un projet).

L'architecture du projet est très simple. Il s'agit d'un projet J2EE "classique" dans lequel j'utilise le framework ZK (car l'application est une RIA). Mais ZK n'est lié en aucun cas à la persistance et c'est sur ce point justement que j'ai besoin de votre aide. Pour la persistance justement, j'utilise EclipseLink. Grosso modo, j'ai donc simplement un fichier WEB-INF/persistence.xml et mes POJO @Entity.

J'ai besoin de votre aide pour me confirmer que l'architecture actuelle ne permettra pas d'avoir des pertes de données si deux utilisateurs connectés tentent de modifier un même objet ! Voilà tout. Donc je vais vous présenter comment je m'y prend au niveau de la persistance, d'une façon très simple. Et j'ai besoin que vous me disiez si certains points peuvent poser problème.

Le premier point de la persistence de mon application consiste a créer un EntityManager dans une classe singleton que j'ai nommé affectusement PersistenceFactory :
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
public final class PersistenceFactory {
 
  private static PersistenceFactory instance;
  private final EntityManager em;
 
  public static PersistenceFactory getInstance() {
    if (PersistenceFactory.instance == null) {
      PersistenceFactory.instance = new PersistenceFactory();
    }
    return PersistenceFactory.instance;
  }
 
  private PersistenceFactory() {
    this.em = Persistence.createEntityManagerFactory("monprojet").createEntityManager();
  }
 
  public EntityManager getEm() {
    return this.em;
  }
}
Déjà j'aimerais votre confirmation sur le fait qu'il ne faille qu'un seul EntityManager dans l'application ?

Seconde étape, j'ai créé un DAO générique, le bien nommé GenericDAO :
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
142
143
144
145
146
147
148
149
public class GenericDAO<T> implements IGenericDAO<T> {
 
  private final Class<?>      clazz;
  protected EntityManager     em;
 
  public GenericDAO(final Class<?> clazz) {
    this.clazz = clazz;
    this.em = PersistenceFactory.getInstance().getEm();
  }
 
  public T find(final Object id) {
    if (id == null) {
      return null;
    }
    T entity = null;
    if (this.clazz != null) {
      try {
        entity = (T) this.em.find(this.clazz, id);
      } catch (final Exception e) {}
    }
    return entity;
  }
 
  public List<T> getAll() {
    return this.getAll(null);
  }
 
  public List<T> getAll(final String sqlMore) {
    List<T> entities = null;
    if (this.clazz != null) {
      String q = "SELECT o FROM " + this.clazz.getSimpleName() + " o";
      if (StringUtils.isNotBlank(sqlMore)) {
        q += " " + sqlMore;
      }
      try {
        entities = this.em.createQuery(q).getResultList();
      } catch (final Exception e) {}
    }
    return entities;
  }
 
  public void persist(final T entity) {
    this.persist(entity, false);
  }
 
  public void persist(final T entity, final boolean refresh) {
    if (entity == null) {
      return;
    }
    synchronized (this.em) {
      try {
        this.em.getTransaction().begin();
        if (!this.em.contains(entity)) {
          this.em.persist(entity);
        } else {
          this.em.merge(entity);
        }
        this.em.getTransaction().commit();
        if (refresh) {
          this.refresh(entity);
        }
      } catch (final Exception e) {}
    }
  }
 
  public void persist(final T[] entities) {
    for (final T entity : entities) {
      this.persist(entity);
    }
  }
 
  public void persist(final T[] entities, final boolean refresh) {
    for (final T entity : entities) {
      this.persist(entity, refresh);
    }
  }
 
  public void refresh(final T entity) {
    if (entity == null) {
      return;
    }
    synchronized (this.em) {
      if (this.em.contains(entity)) {
        try {
          this.em.refresh(entity);
        } catch (final Exception e) {}
      }
    }
  }
 
  public void refresh(final T[] entities) {
    for (final T entity : entities) {
      this.refresh(entity);
    }
  }
 
  public void remove(final T entity) {
    if (entity == null) {
      return;
    }
    synchronized (this.em) {
      try {
        this.em.getTransaction().begin();
        this.em.remove(entity);
        this.em.getTransaction().commit();
      } catch (final Exception e) {}
    }
  }
 
  public void remove(final T[] entities) {
    for (final T entity : entities) {
      this.remove(entity);
    }
  }
 
  public void removeAll() {
    final List<T> entities = this.getAll();
    synchronized (this.em) {
      try {
        this.em.getTransaction().begin();
        for (final T entity : entities) {
          this.em.remove(entity);
        }
        this.em.getTransaction().commit();
      } catch (final Exception e) {}
    }
  }
 
  @Deprecated
  /**
   * Prefer persist
   */
  public void update(final T entity) {
    if (entity == null) {
      return;
    }
    this.persist(entity);
  }
 
  @Deprecated
  /**
   * Prefer persist
   */
  public void update(final T[] entities) {
    for (final T entity : entities) {
      this.update(entity);
    }
  }
}
Comme vous pouvez le voir, j'ai mis quelques synchronized... ZK est multithreads. Sont-ils pertinents ?

Du coup, quand je veux créer un DAO pour la classe Project, je dérive du DAO générique (et je peux ensuite créer les méthodes spécifiques à la classe) :
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
public final class ProjectDAO extends GenericDAO<Project> implements IProjectDAO {
 
  private static ProjectDAO instance;
 
  public static ProjectDAO getInstance() {
    if (ProjectDAO.instance == null) {
      ProjectDAO.instance = new ProjectDAO();
    }
    return ProjectDAO.instance;
  }
 
  private ProjectDAO() {
    super(Project.class);
  }
 
  public Project findOneByName(final Society society, final String name) {
    Project project = null;
    final String q = "SELECT p FROM Project p WHERE p.name = :name AND p.society = :society";
    final Query query = PersistenceFactory.getInstance().getEm().createQuery(q);
    query.setParameter("name", name);
    query.setParameter("society", society);
    try {
      project = (Project) query.getSingleResult();
    } catch (final NoResultException e) {}
    return project;
  }
 
  // ...
}
Je peux donc faire par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
ProjectDAO.getInstance().persist(unObjProjet); // persiste le projet
Alors, qu'en dîtes-vous ? Est-ce que vous voyez un ou plusieurs problème(s) dans ma façon de faire ? J’aimerais simplement confirmer mon choix ou s'il faut, apporter quelques corrections.

Merci d'avance pour votre aide.