Article intéressant sur le concept. Je me sens d'autant plus concerné que je joue avec de gros projets où de multiples applications font une réutilisation d'un même data model. Et dans ce cas là, les @NamedQuery, c'est très limité, on ne peux pas changer le modèle à chaque fois qu'on veux changer une query dans une appli. Et le xml, c'est galère en refactoring
. Ton article va apporter de l'eau à mon moulin
J'aurais cependant plusieurs remarques:
1) ça a l'air bête, mais le projet maven sur github ne fonctionne pas 'out of the box' et crache des "No Persistence provider". C'est dommage vu qu'il viens en appuis de l'article.
2) VII. Le référenceur programmatique de NamedQuery => Ben je propose "VII. Enrôler les named queries"
3) C'est du chipo, mais lombok peut faire mieux. Pas besoin de répéter les getter / setter, d'ajouter le toString, etc alors que l'annotation @Data faite déjà tout le travail il me semble (mea culpa, je n'ai pas vraiment testé)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Entity
@Table(name="VIDEO_GAME")
@Data
public class VideoGame implements Serializable
{
@GeneratedValue
@Setter(AccessLevel.PRIVATE)
private Long id;
private String name;
@Enumerated(EnumType.STRING)
@Column(name="GAME_GENRE")
private GameGenre gameGenre;
public VideoGame(String name, GameGenre gameGenre)
{
super();
this.name = name;
this.gameGenre = gameGenre;
}
} |
3) Même si une partie très intéressante de l'architecture a été faite, je trouve que la partie enum pourrait être plus élégante facile à maintenir.
Elle contient pas mal de boilerplate code à réécrire sur chaque enum contenant des query custom. Dans ton exemple il n'y a que "VideoGameQuery", mais en pratique on peut se retrouver avec des centaines de query à gérer.
Une solution basique serait de laisser lombok gérer les getters et constructeurs sur le champ query, comme c'est le cas dans l'entity.
Une solution plus élégante à mon gout serait de jouer avec les annotations et les default methods de l'interface
Exemple:
une annotation custom:
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME)
@Target(value={FIELD})
public @interface EnumQuery {
public String query() default "";
} |
une interface avec de la logique:
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
| public interface RegistrableQuery
{
/**
* @return la requête JPQL.
*/
default String getQuery(){
try {
EnumQuery annotation = this.getClass().getField(((Enum<?>) this).name()).getDeclaredAnnotation(EnumQuery.class);
if (annotation != null){
return annotation.query();
} else {
throw new RuntimeException("enum "+this+" does not have a @EnumQuery annotation");
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(this+" is a broken enum according to java specs. Should not happend");
} catch (ClassCastException e) {
throw new RuntimeException(this+" is not an enum");
}
};
/**
* @return l'identifiant de la requête JPQL.
*/
default String getIdentifier() {
return String.format("%s_%s", this.getClass(), ((Enum<?>)this).name());
}
} |
(L'idéal aurait été une abstract class plutot que des methods default, mais enum de m*** oblique quoi
)
L'enum devient alors beaucoup plus simple à lire à mon avis:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public enum VideoGameQuery implements RegistrableQuery
{
/**
* retourne les VideoGame en fonction de leur genre.
* Argument JPQL attendu : gameGenre de type GameGenre.
*/
@EnumQuery(query="SELECT vg FROM VideoGame vg WHERE vg.gameGenre = :gameGenre")
FIND_BY_GENRE,
/**
* retourne les VideoGame en fonction d'un nom approchant (LIKE).
* Argument JPQL attendu : name de type String.
*/
@EnumQuery(query="SELECT vg FROM VideoGame vg WHERE vg.name LIKE :name")
FIND_BY_NAME_LIKE;
} |
3) je ne sais pas compter jusque 4 visiblement.
0 |
0 |