web-dev-qa-db-fra.com

Collection triée JPA / hibernate @OrderBy vs @Sort

J'aimerais avoir une collection d'objets enfants (ici exemple chat-chaton) qui sont commandés. Et gardez leur ordre sur l'ajout de nouveaux éléments.

@Entity 
public class Cat {
  @OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
  @OrderBy("name ASC")
  private List<Kitten> kittens;

  public void setKittens(List<Kitten> kittens) { this.kittens = kittens; }
  public List<Kitten> getKittens() { return kittens; } 
}

Quand je fais cat.getKittens.add(newKitten) l'ordre par nom sera rompu.

Est-il possible de laisser hibernation faire le travail de garder la collection toujours commandée? En utilisant l'annotation @ Sort hibernate ?
@ Sort a l'inconvénient de vous obliger à implémenter une interface comparable ... Quelle serait la bonne façon de faire "JPA pur"? Tout sauvegarder dans DB et le recharger? Est-il logique de combiner @OrderBy et @Sort?

Mise à jour La solution jusqu'à présent consiste à combiner @OrderBy et @Sort. @OrderBy conduit à une clause ORDER BY Dans le SQL généré qui est meilleure pour les performances (je suppose que Java est à nouveau "tri" lors de l'insertion dans le conteneur trié, mais cela devrait être beaucoup plus rapide car les éléments sont déjà triés) @Sort avec une interface Comparable implémentée conduit à un conteneur toujours trié. Notez que j'utilise maintenant SortedSet au lieu de List. Voici le code mis à jour:

@Entity 
public class Cat {
  @OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
  @OrderBy("name ASC")
  @Sort(type = SortType.NATURAL)
  private SortedSet<Kitten> kittens;

  public void setKittens(SortedSet<Kitten> kittens) { this.kittens = kittens; }
  public SortedSet<Kitten> getKittens() { return kittens; } 
}
31
adler

Si vous souhaitez éviter les annotations non standard, vous pouvez faire en sorte que kittens utilise une implémentation triée de Collection. Cela garantirait que kittens est toujours dans l'ordre trié. Quelque chose comme ça:

@Entity 
public class Cat {
  @OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
  @OrderBy("name ASC")
  private SortedSet<Kitten> kittens = new TreeSet<>();
}

Notez que cette approche nécessite également Kitten pour implémenter Comparable (vous pouvez également passer un Comparator à votre constructeur TreeSet). De plus, j'utilise un Set parce que je ne connais aucune implémentation standard triée de List et je suppose que le Cat n'a pas de clones dans sa portée = p.

pdate: Je ne sais pas à quel point Hibernate est difficile avec ses définitions getter/setter, mais avec EclipseLink, j'ai pu supprimer un setter entièrement et encapsuler le List retourné par mon getter dans un appel Collections.unmodifiableList(...). J'ai ensuite défini des méthodes spéciales pour modifier la collection. Vous pouvez faire la même chose et forcer les appelants à utiliser une méthode add qui insère les éléments dans l'ordre trié. Si Hibernate se plaint de ne pas avoir le getter/setter, peut-être pourriez-vous changer le modificateur d'accès? Je suppose que cela dépend de jusqu'où vous êtes prêt à aller pour éviter les dépendances non standard.

22
DannyMo

La dernière version d'Hibernate utilise de nouvelles annotations pour ce faire:

@SortNatural
@OrderBy("name ASC")
private SortedSet<Kitten> kittens = new TreeSet<>();

Il y a deux parties à cela:

  1. L'annotation @OrderBy Spécifie qu'une clause order by Doit être ajoutée à la requête de base de données lors de la récupération des enregistrements associés.
  2. L'annotation @SortNatural Suppose que Kitten implémente l'interface Comparable et utilise ces informations lors de la construction de l'instance TreeSet. Notez que vous pouvez remplacer cela par l'annotation @SortComparator, Qui vous permet de spécifier une classe Comparator qui sera transmise au constructeur TreeSet.

Voir la documentation: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#collections-sorted-set

17
mark.monteiro