Çoktan çoğa ilişkilendirme tablosunu ekstra sütunlarla eşleme


131

Veritabanım 3 tablo içeriyor: Kullanıcı ve Hizmet varlıkları çoktan çoğa ilişkiye sahiptir ve aşağıdaki gibi SERVICE_USER tablosuyla birleştirilir:

KULLANICILAR - SERVICE_USER - SERVICES

SERVICE_USER tablosu ek BLOCKED sütun içerir.

Böyle bir haritalama yapmanın en iyi yolu nedir? Bunlar Varlık sınıflarım

@Entity
@Table(name = "USERS")
public class User implements java.io.Serializable {

private String userid;
private String email;

@Id
@Column(name = "USERID", unique = true, nullable = false,)
public String getUserid() {
return this.userid;
}

.... some get/set methods
}

@Entity
@Table(name = "SERVICES")
public class CmsService implements java.io.Serializable {
private String serviceCode;

@Id
@Column(name = "SERVICE_CODE", unique = true, nullable = false, length = 100)
public String getServiceCode() {
return this.serviceCode;
}
.... some additional fields and get/set methods
}

Bu örneği takip ettim http://giannigar.wordpress.com/2009/09/04/m ... using-jpa / İşte bazı test kodları:

User user = new User();
user.setEmail("e2");
user.setUserid("ui2");
user.setPassword("p2");

CmsService service= new CmsService("cd2","name2");

List<UserService> userServiceList = new ArrayList<UserService>();

UserService userService = new UserService();
userService.setService(service);
userService.setUser(user);
userService.setBlocked(true);
service.getUserServices().add(userService);

userDAO.save(user);

Sorun, hazırda bekletmenin Kullanıcı nesnesi ve Kullanıcı Hizmeti 1 olarak kalmasıdır. CmsService nesnesiyle başarı yok

EAGER getirmeyi kullanmayı denedim - ilerleme yok

Beklediğim davranışı yukarıda verilen haritalama ile gerçekleştirmek mümkün mü?

Belki de ek sütunla çoktan çoğuna birleştirme tablosunu eşlemenin daha zarif bir yolu vardır?

Yanıtlar:


192

SERVICE_USER tablosu tam bir birleştirme tablosu olmadığı, ancak ek işlevsel alanları (engellenmiş) olduğu için, onu bir varlık olarak eşlemeniz ve Kullanıcı ve Hizmet arasındaki çoktan çoğa ilişkiyi iki OneToMany ilişkilendirmesine ayırmanız gerekir: Bir Kullanıcının birçok Kullanıcı Hizmeti vardır, ve bir Hizmetin birçok Kullanıcı Hizmeti vardır.

Bize en önemli kısmı göstermediniz: varlıklarınız arasındaki ilişkilerin haritalanması ve ilklendirilmesi (yani sorun yaşadığınız kısım). Bu yüzden size nasıl görünmesi gerektiğini göstereceğim.

İlişkileri çift yönlü yaparsanız, buna sahip olmalısınız.

class User {
    @OneToMany(mappedBy = "user")
    private Set<UserService> userServices = new HashSet<UserService>();
}

class UserService {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "service_code")
    private Service service;

    @Column(name = "blocked")
    private boolean blocked;
}

class Service {
    @OneToMany(mappedBy = "service")
    private Set<UserService> userServices = new HashSet<UserService>();
}

İlişkilerinize herhangi bir basamak koymazsanız, tüm varlıkları sürdürmeniz / kaydetmeniz gerekir. İlişkinin yalnızca sahibi olan tarafın (burada, Kullanıcı Hizmeti tarafı) başlatılması gerekmesine rağmen, her iki tarafın da tutarlı olduğundan emin olmak da iyi bir uygulamadır.

User user = new User();
Service service = new Service();
UserService userService = new UserService();

user.addUserService(userService);
userService.setUser(user);

service.addUserService(userService);
userService.setService(service);

session.save(user);
session.save(service);
session.save(userService);

2
Eklemek gerekirse .. Bence en iyi yol bu olsa da (performans nedenlerinden ötürü her zaman FK'ye sahip olan şeyi bir varlık olarak haritalamayı tercih ederim), aslında tek yol bu değil. Ayrıca SERVICE_USER tablosundaki değerleri bir bileşen (JPA'nın gömülebilir olarak adlandırdığı) olarak eşleyebilir @ElementCollectionve Kullanıcı ve Hizmet varlıklarından birinden (veya her ikisinden) bir bileşen kullanabilirsiniz .
Steve Ebersole

6
UserService tablosunun birincil anahtarı ne olacak? Kullanıcı ve hizmet yabancı anahtarlarının birleşimi olmalıdır. Haritalı mı?
Jonas Gröger

24
Ben öyle yapmazdım. Bileşik anahtarlar zahmetli, verimsizdir ve Hazırda Bekletme, bileşik anahtarların kullanılmamasını önerir. Diğer herhangi bir varlık için olduğu gibi sadece otomatik olarak oluşturulan bir kimlik kullanın ve hayat çok daha kolay olacaktır. Birliğini sağlamak için [userFK, serviceFK]benzersiz bir kısıtlama kullanın.
JB Nizet

1
@GaryKephart: Kendi kodunuzla ve kendi haritanızla kendi sorunuzu sorun.
JB Nizet

1
@gstackoverflow: Hibernate 4 bu konuda hiçbir şeyi değiştirmez. Bunun ne kadar uygunsuz olduğunu gerçekten anlamıyorum.
JB Nizet

5

Xml dosyaları yapılandırmasında hazırda bekletme ile çoktan çoğa ilişki tablosunu fazladan sütun (lar) la eşlemenin bir yolunu arıyorum.

'A' ve 'c' tablosunun 'ekstra' adlı bir sütunla çoktan çoğa ilişkilendirilmiş iki tablosu olduğunu varsayarsak. Çünkü tam bir örnek bulamadım, işte kodum. Umarım yardımcı olur :).

Birincisi, Java nesneleri.

public class A implements Serializable{  

    protected int id;
    // put some others fields if needed ...   
    private Set<AC> ac = new HashSet<AC>();

    public A(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Set<AC> getAC() {
        return ac;
    }

    public void setAC(Set<AC> ac) {
        this.ac = ac;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 97;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof A))
            return false;
        final A other = (A) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

public class C implements Serializable{

    protected int id;
    // put some others fields if needed ...    

    public C(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 98;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof C))
            return false;
        final C other = (C) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

Şimdi ilişki tablosunu oluşturmalıyız. İlk adım, karmaşık bir birincil anahtarı (a.id, c.id) temsil eden bir nesne oluşturmaktır.

public class ACId implements Serializable{

    private A a;
    private C c;

    public ACId() {
        super();
    }

    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public C getC() {
        return c;
    }
    public void setC(C c) {
        this.c = c;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result
                + ((c == null) ? 0 : c.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ACId other = (ACId) obj;
        if (a == null) {
            if (other.a != null)
                return false;
        } else if (!a.equals(other.a))
            return false;
        if (c == null) {
            if (other.c != null)
                return false;
        } else if (!c.equals(other.c))
            return false;
        return true;
    }
}

Şimdi ilişkilendirme nesnesinin kendisini oluşturalım.

public class AC implements java.io.Serializable{

    private ACId id = new ACId();
    private String extra;

    public AC(){

    }

    public ACId getId() {
        return id;
    }

    public void setId(ACId id) {
        this.id = id;
    }

    public A getA(){
        return getId().getA();
    }

    public C getC(){
        return getId().getC();
    }

    public void setC(C C){
        getId().setC(C);
    }

    public void setA(A A){
        getId().setA(A);
    }

    public String getExtra() {
        return extra;
    }

    public void setExtra(String extra) {
        this.extra = extra;
    }

    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        AC that = (AC) o;

        if (getId() != null ? !getId().equals(that.getId())
                : that.getId() != null)
            return false;

        return true;
    }

    public int hashCode() {
        return (getId() != null ? getId().hashCode() : 0);
    }
}

Bu noktada, tüm sınıflarımızı hazırda bekletme xml yapılandırmasıyla eşleştirme zamanı.

A.hbm.xml ve C.hxml.xml (aynı sessizlik).

<class name="A" table="a">
        <id name="id" column="id_a" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">a_id_seq</param>
            </generator>
        </id>
<!-- here you should map all others table columns -->
<!-- <property name="otherprop" column="otherprop" type="string" access="field" /> -->
    <set name="ac" table="a_c" lazy="true" access="field" fetch="select" cascade="all">
        <key>
            <column name="id_a" not-null="true" />
        </key>
        <one-to-many class="AC" />
    </set>
</class>

<class name="C" table="c">
        <id name="id" column="id_c" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">c_id_seq</param>
            </generator>
        </id>
</class>

Ve sonra ilişkilendirme eşleme dosyası, a_c.hbm.xml.

<class name="AC" table="a_c">
    <composite-id name="id" class="ACId">
        <key-many-to-one name="a" class="A" column="id_a" />
        <key-many-to-one name="c" class="C" column="id_c" />
    </composite-id>
    <property name="extra" type="string" column="extra" />
</class>

İşte test edilecek kod örneği.

A = ADao.get(1);
C = CDao.get(1);

if(A != null && C != null){
    boolean exists = false;
            // just check if it's updated or not
    for(AC a : a.getAC()){
        if(a.getC().equals(c)){
            // update field
            a.setExtra("extra updated");
            exists = true;
            break;
        }
    }

    // add 
    if(!exists){
        ACId idAC = new ACId();
        idAC.setA(a);
        idAC.setC(c);

        AC AC = new AC();
        AC.setId(idAC);
        AC.setExtra("extra added"); 
        a.getAC().add(AC);
    }

    ADao.save(A);
}

1

Daha önce belirtildiği gibi, JPA ile fazladan sütun alma şansına sahip olmak için, tek bir ManyToMany ilişkisi yerine iki OneToMany ilişkilendirmesi kullanmanız gerekir. Otomatik olarak oluşturulmuş değerlere sahip bir sütun da ekleyebilirsiniz; bu şekilde, eğer yararlıysa, tablonun birincil anahtarı olarak çalışabilir.

Örneğin, extra sınıfın uygulama kodu şöyle görünmelidir:

@Entity
@Table(name = "USER_SERVICES")
public class UserService{

    // example of auto-generated ID
    @Id
    @Column(name = "USER_SERVICES_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long userServiceID;



    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "USER_ID")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SERVICE_ID")
    private Service service;



    // example of extra column
    @Column(name="VISIBILITY")    
    private boolean visibility;



    public long getUserServiceID() {
        return userServiceID;
    }


    public User getUser() {
        return user;
    }

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

    public Service getService() {
        return service;
    }

    public void setService(Service service) {
        this.service = service;
    }

    public boolean getVisibility() {
        return visibility;
    }

    public void setVisibility(boolean visibility) {
        this.visibility = visibility;
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.