2016-03-18 17 views
7

Próbuję wykonać wiele złączeń za pomocą klucza złożonego. Używam aliasów, aby wymusić tworzenie sprzężenia, ale wydaje się, że sprzężenie nie jest generowane przez Hibernate. Nie wiem, dlaczego tak się dzieje. Mogę go uruchomić z natywnym zapytaniem SQL, ale nie przy użyciu kryteriów.Kryteria klucza kompilacji hibernacji Dołącz

podejrzewam to może mieć do czynienia z drogi jak kompozytowe kluczowe definicje są odwzorowywane (CF associationOverrides na BusinessServiceUser)

poniżej są moje klasy modelu domeny i informacji zapytania. Wszelkie pomysły są mile widziane :)

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; 
    } 
... 
} 

użytkownika

@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); 

... 
} 

Rola

@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); 
... 
} 

Zapytanie Kryteria DAO

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 wygenerowane zapytanie

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 

Błąd zapytania

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

robocza Język SQL

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(); 

Specyfikacja

  • Hibernate 3.6.10.Final
  • JPA 2.0
  • Wiosna 4.0.0
  • wersja sterownika Oracle JDBC 10.2.0.3.0

Odpowiedz

3

Po pierwsze, dlaczego nie spróbować zmniejszyć do minimalistic example? Próbujesz angażować wiele podmiotów i relacji, dlaczego nie zmniejszyć go, nawet tylko ze względu na własny czas rozwiązywania problemów?

Po drugie, Twój kod nie jest kompletny, nie ma identyfikatora użytkownika i innych podmiotów. Dla celów odpowiedzi przyjmuję, że id jest gdzieś zdefiniowany.

Będę udzielał odpowiedzi bez usług biznesowych i ról, myślę, że podobne rozwiązanie będzie miało zastosowanie.

Jak podejść do rozwiązania problemu?

Najpierw należy ograniczyć do najprostszych kryteriów i zestawu jednostek. Na przykład ograniczenie 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]")); 

Generated zapytań SQL:

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=? 

Oczywiście oczekiwany dołączyć brakuje (więc nie trzeba skomplikowanej przykład odtworzyć problem) .

Analizując BusinessServiceUserId, używa @Embedded z @ManyToOne. Zauważ, że jest to rozszerzenie specyficzne dla Hibernacji, ogólnie nie powinieneś używać @ManyToOne w @Embedded. Spróbujmy zwykły kwerendy zamiast kryteriów:

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

Wygenerowano 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, łączenia ma. Masz tu co najmniej jedno rozwiązanie - użyj zapytania zamiast kryteriów. Bardziej skomplikowane zapytania można tworzyć z łączeniem pobierania itp.

Teraz kryteria. Najpierw sprawdźmy za pomocą konwencjonalnego standardowego mapowania. Przy standardowym odwzorowaniu nie można odwoływać się w ten sposób do @ManyToOne zdefiniowanego w @Embedded. Dodajmy mapowania do klasy samej BusinessServiceUser zamiast @Transient

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

Uwaga ta dodatkowa mapowanie nie kosztuje.

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]")); 

Generated 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=? 

Więc tutaj masz rozwiązanie 2 z kryteriami. Dodaj odwzorowania w encji i użyj ich w kryteriach zamiast złożonego pk.

Chociaż nie jestem świadomy konfiguracji do użycia @EmbeddedId pk z @AssotiationOverride, kryteria i dołącz pobieranie w dokładnie taki sam sposób, jak próbujesz zrobić, prawdopodobnie to i tak nie jest najlepsze podejście.

Powiązane problemy