Quelle est la manière la plus intelligente d'obtenir une entité avec un champ de type Liste persistante?
Command.java
package persistlistofstring;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Persistence;
@Entity
public class Command implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
@Basic
List<String> arguments = new ArrayList<String>();
public static void main(String[] args) {
Command command = new Command();
EntityManager em = Persistence
.createEntityManagerFactory("pu")
.createEntityManager();
em.getTransaction().begin();
em.persist(command);
em.getTransaction().commit();
em.close();
System.out.println("Persisted with id=" + command.id);
}
}
Ce code produit:
> Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named pu: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory:
> oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Local Exception Stack:
> Exception [TOPLINK-30005] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
> Internal Exception: javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
> Exception Description: predeploy for PersistenceUnit [pu] failed.
> Internal Exception: Exception [TOPLINK-7155] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
> Exception Description: The type [interface java.util.List] for the attribute [arguments] on the entity class [class persistlistofstring.Command] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
> at oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:143)
> at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:169)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
> at persistlistofstring.Command.main(Command.java:30)
> Caused by:
> ...
Désolé de relancer un ancien fil de discussion, mais si quelqu'un cherche une solution alternative où vous stockez vos listes de chaînes dans un champ de votre base de données, voici comment j'ai résolu cela. Créez un convertisseur comme celui-ci:
Maintenant, utilisez-le sur vos entités comme ceci:
Dans la base de données, votre liste sera stockée sous la forme foo; bar; foobar et dans votre objet Java, vous obtiendrez une liste avec ces chaînes.
J'espère que cela est utile à quelqu'un.
la source
SPLIT_CHAR
occurrences dans votre chaîne.Cette réponse a été faite avec des implémentations pré-JPA2, si vous utilisez JPA2, voir la réponse ElementCollection ci-dessus:
Les listes d'objets à l'intérieur d'un objet modèle sont généralement considérées comme des relations «OneToMany» avec un autre objet. Cependant, une chaîne n'est pas (par elle-même) un client autorisé d'une relation un-à-plusieurs, car elle n'a pas d'ID.
Alors tu devrais convertir votre liste de chaînes à une liste de JPA Argument classe Les objets contenant un identifiant et une chaîne. Vous pourriez potentiellement utiliser la chaîne comme ID, ce qui économiserait un peu d'espace dans votre table à la fois en supprimant le champ ID et en consolidant les lignes où les chaînes sont égales, mais vous perdriez la possibilité de réorganiser les arguments dans leur ordre d'origine (car vous n'avez stocké aucune information de commande).
Vous pouvez également convertir votre liste en @Transient et ajouter un autre champ (argStorage) à votre classe qui est un VARCHAR () ou un CLOB. Vous devrez ensuite ajouter 3 fonctions: 2 d'entre elles sont identiques et devraient convertir votre liste de chaînes en une seule chaîne (dans argStorage) délimitée de manière à pouvoir les séparer facilement. Annotez ces deux fonctions (qui font chacune la même chose) avec @PrePersist et @PreUpdate. Enfin, ajoutez à nouveau la troisième fonction qui divise argStorage dans la liste des chaînes et annotez-la @PostLoad. Cela gardera votre CLOB mis à jour avec les chaînes chaque fois que vous allez stocker la commande et gardera le champ argStorage mis à jour avant de le stocker dans la base de données.
Je suggère toujours de faire le premier cas. C'est une bonne pratique pour de vraies relations plus tard.
la source
Selon Java Persistence avec Hibernate
Si vous utilisiez Hibernate, vous pourriez faire quelque chose comme:
Mise à jour: Notez que ceci est maintenant disponible dans JPA2.
la source
Nous pouvons également l'utiliser.
la source
J'avais le même problème donc j'ai investi la solution possible donnée mais à la fin j'ai décidé de mettre en œuvre mon ';' liste séparée de String.
donc j'ai
De cette façon, la liste est facilement lisible / modifiable dans la table de la base de données;
la source
arguments
est une liste d'autorisations d'accès, le caractère spécial, aseparator
, peut être vulnérable aux attaques d'escalade de privilèges.;
ce qui cassera votre application.Lors de l'utilisation de l'implémentation Hibernate de JPA, j'ai constaté que le simple fait de déclarer le type en tant que ArrayList au lieu de List permet à hibernate de stocker la liste des données.
De toute évidence, cela présente un certain nombre d'inconvénients par rapport à la création d'une liste d'objets Entity. Pas de chargement paresseux, pas de possibilité de référencer les entités de la liste à partir d'autres objets, peut-être plus de difficulté à construire des requêtes de base de données. Cependant, lorsque vous avez affaire à des listes de types assez primitifs que vous voudrez toujours chercher avec impatience avec l'entité, alors cette approche me semble bien.
la source
@OneToMany
@ManyToOne
@ElementCollection
cela vous donnera uneCaused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements
exception au démarrage du serveur. Parce que hibernates veut que vous utilisiez des interfaces de collecte.Il semble qu'aucune des réponses n'ait exploré les paramètres les plus importants pour un
@ElementCollection
mappage.Lorsque vous mappez une liste avec cette annotation et que JPA / Hibernate génère automatiquement les tables, les colonnes, etc., il utilisera également des noms générés automatiquement.
Alors, analysons un exemple de base:
@ElementCollection
annotation de base (où vous pouvez définir lefetch
ettargetClass
préférences)@CollectionTable
annotation est très utile quand il s'agit de donner un nom à la table qui va générer, ainsi que des définitions comme cellejoinColumns
,foreignKey
l »,indexes
,uniqueConstraints
, etc.@Column
est important de définir le nom de la colonne qui stockera lavarchar
valeur de la liste.La création DDL générée serait:
la source
Ok je sais que c'est un peu tard. Mais pour ces âmes courageuses qui verront cela avec le temps.
Comme écrit dans la documentation :
La partie importante est type qui implémente Serializable
Donc, de loin, la solution la plus simple et la plus facile à utiliser consiste simplement à utiliser ArrayList au lieu de List (ou de tout conteneur sérialisable):
Rappelez-vous cependant que cela utilisera la sérialisation du système, donc cela viendra avec un certain prix, tel que:
si le modèle d'objet sérialisé change, vous ne pourrez peut-être pas restaurer les données
une petite surcharge est ajoutée pour chaque élément stocké.
En bref
il est assez simple de stocker des drapeaux ou quelques éléments, mais je ne le recommanderais pas pour stocker des données qui pourraient grossir.
la source
La réponse de Thiago est correcte, en ajoutant un échantillon plus spécifique à la question, @ElementCollection créera une nouvelle table dans votre base de données, mais sans mapper deux tables, cela signifie que la collection n'est pas une collection d'entités, mais une collection de types simples (chaînes, etc. .) ou une collection d'éléments intégrables (classe annotée avec @Embeddable ).
Voici l'exemple pour conserver la liste de String
Voici l'exemple pour conserver la liste des objets personnalisés
Pour ce cas, nous devons rendre la classe Embeddable
la source
Voici la solution pour stocker un Set à l'aide de @Converter et StringTokenizer. Un peu plus de vérifications par rapport à la solution @ jonck-van-der-kogel .
Dans votre classe Entity:
StringSetConverter:
la source
Mon correctif pour ce problème était de séparer la clé primaire avec la clé étrangère. Si vous utilisez eclipse et avez apporté les modifications ci-dessus, n'oubliez pas d'actualiser l'explorateur de base de données. Puis recréez les entités à partir des tables.
la source