JPA / Hibernate: @ManyToOne et @ OneToOne relationships tagged as FetchType.Paresseux et optionnel = false Ne pas charger paresseusement sur em.trouver()?
j'ai l'entité ci-dessous (uniquement les mappages):
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY) // lazy XToOne
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Tendering tendering;
...
}
notez les commentaires ci-dessus: il y a trois @XToOne
relations avec d'autres entités:
Utilisateur (un SecurityIdentity sous-classe avec un simple ID PK, référencé par PQ, représentant le propriétaire):
@Entity
@Table(name = "Users")
@DiscriminatorValue(value = "user")
public class User extends SecurityIdentity
{
@Column
private String name;
@OneToMany(mappedBy = "user")
private Set<PQ> pqs = new HashSet<PQ>();
...
}
Groupe (aussi une sous-classe SecurityIdentity avec un simple ID comme PK, fait référence au PQ pour représenter un ensemble d'utilisateurs qui peuvent interagir avec qui PQ):
@Entity
@Table(name = "Groups")
@DiscriminatorValue(value = "group")
public class Group extends SecurityIdentity
{
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
appel d'Offres:
@Entity
@Table(name = "Tenderings")
public class Tendering implements Serializable
{
@Id
@Column(name = "pq_id", insertable = false, updatable = false)
private Integer pqId;
@Column(name = "external_code")
private String externalCode;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
ne vous méprenez pas sur les groupes et les utilisateurs qui partagent des identifiants, traitez-les simplement comme de simples identifiants. Un appel d'offres n'est qu'un document distinct (un à un).
Comme vous pouvez le voir il y a trois @XToOne
relations sur l'entité PQ, qui, si aucun type fetch n'était défini, serait chargé avec empressement (par défaut JPA). Donc pour empêcher cela j'ai étiqueté tout @XToOne
relations FetchType.LAZY
.
maintenant quand vous utilisez
em.find(PQ.class, someExistingId);
je l'Hibernation de sortie:
23:53:55,815 INFO [stdout] Hibernate: select pq0_.id as id291_0_, pq0_.description as descript2_291_0_, pq0_.name as name291_0_, pq0_.submission_date as submission4_291_0_, pq0_.user_id as user5_291_0_ from PQs pq0_ where pq0_.id=?
23:53:55,818 INFO [stdout] Hibernate: select user0_.id as id280_0_, user0_1_.identity_type_id as identity2_280_0_, user0_.is_enabled as is1_297_0_, user0_.name as name297_0_, user0_.password as password297_0_, user0_.person_id as person5_297_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
23:53:55,821 INFO [stdout] Hibernate: select group0_.id as id280_0_, group0_1_.identity_type_id as identity2_280_0_, group0_.pq_id as pq2_281_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
23:53:55,823 INFO [stdout] Hibernate: select tendering0_.pq_id as pq1_296_0_, tendering0_.binary_file as binary2_296_0_, tendering0_.customer_id as customer6_296_0_, tendering0_.description as descript3_296_0_, tendering0_.external_code as external4_296_0_, tendering0_.title as title296_0_ from Tenderings tendering0_ where tendering0_.pq_id=?
les trois sélections supplémentaires proviennent des relations @XToOne (comme décrit à de nombreux endroits sur le net). La source que j'étais en train de regarder est la plupart du temps ceci:
Faire un OneToOne-rapport lazy
comme il est mentionné ici, le @ManyToOne
relation User user
should not be fetched:
@ManyToOne(fetch=FetchType.LAZY)
devrait fonctionner parfaitement.
... ici, la relation de l' PQUtilisateur, mais tiré comme vous pouvez le voir de la select user0_.id as id280_0_, ...
déclaration...
Pour les deux autres Group group
et Tendering tendering
, les @OneToOne
inverse mappings, les clés étrangères renvoient à la PK (ID) de la table PQs, ce qui donne la même correspondance dans l'entité PQ.
notez que les trois relations ne sont pas optionnels: un PQ a toujours un propriétaire (Utilisateur), et un PQ est toujours référencé par un appel d'offres et une entité de groupe. Je n'avais pas encore modélisé ça à JPA...
donc, en ajoutant optional = false
pour les trois relations du PQ entité:
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Tendering tendering;
...
}
... J'ai le Hibernate de sortie:
00:47:34,397 INFO [stdout] Hibernate: select pq0_.id as id361_0_, pq0_.description as descript2_361_0_, pq0_.name as name361_0_, pq0_.submission_date as submission4_361_0_, pq0_.user_id as user5_361_0_ from PQs pq0_ where pq0_.id=?
00:47:34,410 INFO [stdout] Hibernate: select user0_.id as id350_0_, user0_1_.identity_type_id as identity2_350_0_, user0_.is_enabled as is1_367_0_, user0_.name as name367_0_, user0_.password as password367_0_, user0_.person_id as person5_367_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
00:47:34,413 INFO [stdout] Hibernate: select group0_.id as id350_0_, group0_1_.identity_type_id as identity2_350_0_, group0_.pq_id as pq2_351_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
Remarque, que je n'ai été jouer avec le optional = false
sur l'entité PQ, car c'est celle que j'utilise dans em.find(...)
. (Si ce n'est pas suffisant, veuillez de m'éclairer.)
Ma question est maintenant deux fois:
- Pourquoi
@ManyToOne
User
entité extraite avec impatience (étant donné que c'était, dit-on travailler paresseusement, voir Faire un OneToOne-rapport lazy)? - Pourquoi est seulement le
OneToOne
relationTendering
entité laissée pour compte? Est-ce parce que leTendering
références d'entité du PQ PK colonne de PK lui-même (@Id
Tendering
),Group
entité doesn't (relation régulière avec le PK du PQ)?
Quel est le problème? Comment puis-je rendre ces relations non-optionnelles paresseuses? (sans code-instrumentation ou autres hacks, juste des annotations simples...)
je sais que la paresse n'est qu'une indication pour le fournisseur JPA de faire quelque chose à propos de la paresse du chargement ou non, mais dans ce cas, il semble que quelque chose d'autre ne va pas (car une partie de cela fonctionne).
PS: J'utilise Hibernate 4.0 BETA, la version qui vient avec JBoss 7.0.0.Final ainsi que les annotations JPA seulement (les ci-dessus sont tous compatibles JPA 1.0).
4 réponses
Salut, je ne suis pas sûr de JPA, mais pour many-to-one
et one-to-one
mappages les valeurs paresseuses qui sont supportées hibernent sont proxy
, no-proxy
et false
dont false est par défaut. Vérifiez cette partie de la DTD
lazy="proxy|no-proxy|false"
et vous pouvez le vérifier Lien. Je pense que cela répond à votre première question.
il y a des différences entre les annotations hibernate et les annotations jpa et autant que je sache, hibernate est chargé par défaut sauf dans certains cas. voici une brève discussion:
http://community.jboss.org/wiki/AShortPrimerOnFetchingStrategies
* la relation ToOne implique qu'il doit y avoir (proxy) bean après l'initialisation de l'objet, qui va déclencher select (vous le voyez) dès qu'il est accédé d'une manière ou d'une autre Êtes-vous sûr de ne rien faire avec des objets qui pourraient forcer le chargement?
Hi Kawu vous montrez des SELECTs supplémentaires parce qu'ils vont exécuter des SELECTs pour toutes les entités impliquées, essayez D'utiliser FetchType.PARESSEUX pour défaut Un-à-Plusieurs et dans la plupart des cas, de sorte qu'à l'aide de FETCH JOINDRE pour obtenir des résultats.
j'espère aider, en u, ou quelqu'un qui ont besoin de cette information