2016-03-18 12 views
7

Ich versuche, mehrere Joins über einen zusammengesetzten Schlüssel durchzuführen. Ich verwende Aliase, um die Join-Erstellung zu erzwingen, aber es scheint, als ob der Join nicht von Hibernate generiert wird. Ich weiß nicht, warum das so ist. Ich kann es mit einer nativen SQL-Abfrage arbeiten lassen, aber nicht mit Kriterien.Hibernate Composite-Schlüssel Kriterien Join

Ich vermute, es könnte mit der Art und Weise zu tun, wie die Verbundschlüsseldefinitionen zugeordnet werden (siehe die associationOverrides auf BusinessServiceUser)

Im Folgenden meiner Domain Modellklassen und Abfrage Informationen sind. Alle Ideen sind willkommen :)

Businessservice

@Entity 
@Table(name = "business_services") 
public class BusinessService extends AbstractEntity implements Serializable { 
    @Column(name = "name", unique = true, nullable = false, length = 255) 
    private String name; 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.businessService", cascade = CascadeType.ALL) 
    @ForeignKey(name = "FK_BUSINESS_SERVICE_USERS") 
    private Set<BusinessServiceUser> businessServiceUsers = new HashSet<BusinessServiceUser>(); 
... 
} 

BusinessServiceUser

@Entity 
@Table(name = "REL_BUSINESS_SERVICE_USER") 
@AssociationOverrides({ 
    @AssociationOverride(name = "pk.businessService", joinColumns = @JoinColumn(name = "BUSINESS_SERVICE_ID")), 
    @AssociationOverride(name = "pk.user", joinColumns = @JoinColumn(name = "USER_ID")) }) 
public class BusinessServiceUser implements Serializable { 

    private BusinessServiceUserId pk = new BusinessServiceUserId(); 
    private Boolean master; 

    public BusinessServiceUser() { 

    } 

    @EmbeddedId 
    public BusinessServiceUserId getPk() { 
    return pk; 
    } 

    public void setPk(BusinessServiceUserId pk) { 
    this.pk = pk; 
    } 

    @Transient 
    public User getUser() { 
    return getPk().getUser(); 
    } 

    public void setUser(User user) { 
    getPk().setUser(user); 
    } 

    @Transient 
    public BusinessService getBusinessService() { 
    return getPk().getBusinessService(); 
    } 

    public void setBusinessService(BusinessService businessService) { 
    getPk().setBusinessService(businessService); 
    } 

    public boolean isMaster() { 
    return master; 
    } 

    public void setMaster(boolean master) { 
    this.master = master; 
    } 
... 
} 

BusinessServiceUserId

@Embeddable 
public class BusinessServiceUserId implements Serializable { 

    private User user; 
    private BusinessService businessService; 

    @ManyToOne 
    public User getUser() { 
    return user; 
    } 

    public void setUser(User user) { 
    this.user = user; 
    } 

    @ManyToOne 
    public BusinessService getBusinessService() { 
    return businessService; 
    } 

    public void setBusinessService(BusinessService businessService) { 
    this.businessService = businessService; 
    } 
... 
} 

Benutzer

@Entity 
@Table(name = "USERS") 
public class User extends AbstractEntity implements Serializable { 

    @Column(name = "first_name", nullable = false, length = 50) 
    private String firstName; 

    @Column(name = "last_name", nullable = false, length = 100) 
    private String lastName; 

    @Column(name = "email_address", unique = true, nullable = false, length = 150) 
    private String emailAddress; 

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.DETACH, targetEntity = Role.class) 
    @JoinTable(name = "REL_USER_ROLE", joinColumns = @JoinColumn(name = "USER_ID", nullable = false) , inverseJoinColumns = @JoinColumn(name = "ROLE_ID", nullable = false)) 
    @ForeignKey(name = "FK_USER_ROLE") 
    private Set<Role> roles = new HashSet<Role>(0); 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user") 
    @ForeignKey(name = "FK_USER_BUSINESS_SERVICE") 
    private Set<BusinessServiceUser> businessServiceUsers = new HashSet<BusinessServiceUser>(0); 

... 
} 

Rolle

@Entity 
@Table(name = "role") 
public class Role extends AbstractEntity implements Serializable { 

    @Enumerated(EnumType.STRING) 
    @Column(name = "name", unique = true, nullable = false) 
    private RoleType name; 

    @Column(name = "code", unique = true, nullable = false) 
    private String code; 

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles") 
    @ForeignKey(name = "FK_ROLE_USERS") 
    private List<User> users = new ArrayList<User>(0); 
... 
} 

DAO Criteria Abfrage

Criteria criteria = getSession().createCriteria(
      BusinessServiceUser.class); 

criteria.setFetchMode("pk.user", FetchMode.JOIN); 
criteria.createAlias("pk.user", "userAlias", Criteria.LEFT_JOIN); 

criteria.setFetchMode("pk.businessService", FetchMode.JOIN); 
criteria.createAlias("pk.businessService", "bsAlias", Criteria.LEFT_JOIN); 

criteria.setFetchMode("userAlias.roles", FetchMode.JOIN); 
criteria.createAlias("userAlias.roles", "roleAlias"); 

criteria.add(Restrictions.eq("bsAlias.name", businessService.getName())); 
criteria.add(Restrictions.eq("roleAlias.name", RoleType.ROLE1)); 

criteria.addOrder(Order.asc("master")); 
return criteria.list(); 

SQL generierte Abfrage

DEBUG org.hibernate.SQL - 
select 
    this_.BUSINESS_SERVICE_ID as BUSINESS2_3_0_, 
    this_.USER_ID as USER3_3_0_, 
    this_.master as master3_0_ 
from 
    REL_BUSINESS_SERVICE_USER this_ 
where 
    bsalias2_.name=? 
    and rolealias3_.name=? 
order by 
    this_.master asc 
Hibernate: 
select 
    this_.BUSINESS_SERVICE_ID as BUSINESS2_3_0_, 
    this_.USER_ID as USER3_3_0_, 
    this_.master as master3_0_ 
from 
    REL_BUSINESS_SERVICE_USER this_ 
where 
    bsalias2_.name=? 
    and rolealias3_.name=? 
order by 
    this_.master asc 

Fehler

java.sql.SQLException: ORA-00904: "ROLEALIAS3_"."NAME": invalid identifier 

Arbeits Native SQL-Abfrage

List<Object[]> result = getSession() 
    .createSQLQuery(
    "select " 
    + " bsu.BUSINESS_SERVICE_ID as bsId, " 
    + " bsu.USER_ID as userId, " 
    + " bsu.master as master, " 
    + " bs.name as business_service, " 
    + " u.first_name as first_name, " 
    + " u.last_name as last_name, " 
    + " u.email_address as email, " 
    + " r.name as role " 
    + "from " 
    + " REL_BUSINESS_SERVICE_USER bsu " 
    + " left outer join users u ON bsu.user_id = u.id " 
    + " left outer join business_services bs ON bsu.business_service_id = bs.id " 
    + " left outer join rel_user_role rur ON u.id = rur.user_id " 
    + " left outer join role r ON rur.role_id = r.id " 
    + "where " 
    + " bs.name = '" + businessService.getName() + "' " 
    + " and r.name like '" + RoleType.ROLE1 + "' " 
    + "order by master asc") 
    .list(); 

Specs

  • Hibernate 3.6.10.Final
  • JPA 2.0
  • Frühling 4.0.0
  • Oracle JDBC-Treiber Version 10.2.0.3.0

Antwort

3

Erstens, warum Sie nicht versuchen, für minimalistic example zu reduzieren? Ihr Beispiel bezieht viele Entitäten und Beziehungen ein, warum reduzieren Sie es nicht, nur um Ihrer eigenen Problemlösung willen?

Zweitens ist Ihr Code nicht vollständig, es fehlt ID auf Benutzer und andere Entitäten. Zur Antwort nehme ich an, dass die ID irgendwo definiert ist.

Ich werde die Antwort ohne Business-Service und Rollen, ich denke, ähnliche Lösung wird gelten.

Wie nähern wir uns, um es zu lösen?

Zuerst auf einfachste Kriterien und Entitätsmenge reduzieren. Zum Beispiel eine Beschränkung BusinessServiceUser.User.emailAddress:

Criteria criteria = session.createCriteria(
      BusinessServiceUser.class, "bu"); 
criteria.setFetchMode("bu.pk.user", FetchMode.JOIN); 
criteria.createAlias("bu.pk.user", "userAlias", Criteria.LEFT_JOIN); 
criteria.add(Restrictions.eq("userAlias.emailAddress", "[email protected]")); 

generiert SQL-Abfrage:

select 
    this_.BUSINESS_SERVICE_ID as BUSINESS3_33_0_, 
    this_.USER_ID as USER2_33_0_, 
    this_.master as master33_0_ 
from 
    REL_BUSINESS_SERVICE_USER this_ 
where 
    useralias1_.email_address=? 

Offensichtlich kommen die erwartete fehlt (so brauchen Sie nicht komplexeres Beispiel, das Problem zu reproduzieren) .

Wenn Sie BusinessServiceUserId betrachten, wird @Embedded mit @ManyToOne verwendet. Beachten Sie, dass dies eine Hibernate-spezifische Erweiterung ist. Im Allgemeinen sollten Sie @ManyToOne nicht in @Embedded verwenden. Lassen Sie uns Klar Abfrage statt Kriterien versuchen:

Query q = session.createQuery("from BusinessServiceUser as u left outer join u.pk.user where u.pk.user.emailAddress='[email protected]'"); 
    q.list(); 

generiert SQL:

select 
    businessse0_.BUSINESS_SERVICE_ID as BUSINESS2_33_0_, 
    businessse0_.USER_ID as USER3_33_0_, 
    user1_.id as id54_1_, 
    businessse0_.master as master33_0_, 
    user1_.email_address as email2_54_1_, 
    user1_.first_name as first3_54_1_, 
    user1_.last_name as last4_54_1_ 
from 
    REL_BUSINESS_SERVICE_USER businessse0_ 
left outer join 
    USERS user1_ 
     on businessse0_.USER_ID=user1_.id 
where 
    user1_.email_address='[email protected]' 

Whoala Join ist da. Sie haben also mindestens eine Lösung - verwenden Sie die Abfrage statt der Kriterien. Komplexere Abfragen können mit Fetch-Join usw. erstellt werden.

Nun zu den Kriterien. Lassen Sie uns zunächst das herkömmliche Standard-Mapping überprüfen. Mit Standard-Mapping können Sie nicht auf die in @Embedded definierte @ManyToOne verweisen. Lassen Sie uns hinzufügen Abbildung auf die BusinessServiceUser Klasse selbst statt @Transient

@ManyToOne(fetch=FetchType.LAZY) 
public User getUser() { 
    return getPk().getUser(); 
} 

Hinweis Diese zusätzliche Zuordnung nicht kostet.

Criteria criteria = session.createCriteria(
      BusinessServiceUser.class, "bu"); 
criteria.setFetchMode("bu.user", FetchMode.JOIN); 
criteria.createAlias("bu.user", "userAlias", Criteria.LEFT_JOIN); 
criteria.add(Restrictions.eq("userAlias.emailAddress", "[email protected]")); 

generiert SQL:

select 
    this_.BUSINESS_SERVICE_ID as BUSINESS3_33_1_, 
    this_.USER_ID as USER2_33_1_, 
    this_.master as master33_1_, 
    this_.user_id as user2_33_1_, 
    useralias1_.id as id54_0_, 
    useralias1_.email_address as email2_54_0_, 
    useralias1_.first_name as first3_54_0_, 
    useralias1_.last_name as last4_54_0_ 
from 
    REL_BUSINESS_SERVICE_USER this_ 
left outer join 
    USERS useralias1_ 
     on this_.user_id=useralias1_.id 
where 
    useralias1_.email_address=? 

So, hier hast du die Lösung 2 mit Kriterien. Fügen Sie Zuordnungen in der Entität hinzu und verwenden Sie sie in Kriterien anstelle von komplexem pk.

Obwohl ich kein Setup kenne, um @EmbeddedId pk mit @AssotiationOverride, Kriterien und Join fetch auf genau die gleiche Weise zu verwenden, wie Sie es versuchen, ist es wahrscheinlich nicht der beste Ansatz.