web-dev-qa-db-de.com

Zuordnung von Viele-zu-Viele-Zuordnungstabelle mit zusätzlichen Spalten

Meine Datenbank enthält 3 Tabellen: Benutzer- und Dienstentitäten haben eine Viele-zu-Viele-Beziehung und werden wie folgt mit der Tabelle SERVICE_USER verknüpft:

USERS - SERVICE_USER - SERVICES

Die Tabelle SERVICE_USER enthält eine zusätzliche Spalte BLOCKED.

Was ist der beste Weg, um ein solches Mapping durchzuführen? Dies sind meine Entitätsklassen

@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
}

Ich folgte diesem Beispiel http://giannigar.wordpress.com/2009/09/04/m ... using-jpa/Hier ist ein Testcode:

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

Das Problem besteht darin, dass im Ruhezustand das Benutzerobjekt und der Benutzerservice eins bleiben. Kein Erfolg mit dem CmsService-Objekt

Ich habe versucht, EAGER Fetch zu verwenden - kein Fortschritt

Ist es möglich, das erwartete Verhalten mit der oben angegebenen Zuordnung zu erreichen?

Vielleicht gibt es eine elegantere Möglichkeit, viele zu vielen Join-Tabellen mit zusätzlichen Spalten zuzuordnen?

129
archie_by

Da die Tabelle SERVICE_USER keine reine Verknüpfungstabelle ist, sondern zusätzliche Funktionsfelder (blockiert) enthält, müssen Sie sie als Entität zuordnen und die Many-to-Many-Zuordnung zwischen Benutzer und Dienst in zwei OneToMany-Zuordnungen aufteilen: Ein Benutzer verfügt über mehrere UserServices. und ein Service hat viele UserServices.

Sie haben uns nicht den wichtigsten Teil gezeigt: die Zuordnung und Initialisierung der Beziehungen zwischen Ihren Entitäten (d. H. Den Teil, mit dem Sie Probleme haben). Also zeige ich dir, wie es aussehen soll.

Wenn Sie die Beziehungen bidirektional machen, sollten Sie also haben

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

Wenn Sie Ihre Beziehungen nicht kaskadieren, müssen Sie alle Entitäten beibehalten/speichern. Obwohl nur die besitzende Seite der Beziehung (hier die UserService-Seite) initialisiert werden muss, ist es auch eine gute Praxis, sicherzustellen, dass beide Seiten kohärent sind.

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);
188
JB Nizet

Ich suche nach einer Möglichkeit, eine Viele-zu-Viele-Zuordnungstabelle mit zusätzlichen Spalten mit Ruhezustand in der XML-Dateikonfiguration zuzuordnen.

Angenommen, Sie haben zwei Tabellen "a" und "c" mit einer Viel-zu-Viel-Zuordnung zu einer Spalte mit dem Namen "extra". Da ich kein vollständiges Beispiel gefunden habe, ist hier mein Code. Hoffe es wird helfen :).

Hier sind zuerst die Java Objekte.

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

}

Jetzt müssen wir die Zuordnungstabelle erstellen. Der erste Schritt besteht darin, ein Objekt zu erstellen, das einen komplexen Primärschlüssel darstellt (a.id, c.id).

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

Nun erstellen wir das Assoziationsobjekt selbst.

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

Zu diesem Zeitpunkt ist es an der Zeit, alle unsere Klassen mit einer XML-Konfiguration im Ruhezustand zuzuordnen.

A.hbm.xml und C.hxml.xml (leise das selbe).

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

Und dann die Assoziationszuordnungsdatei 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>

Hier ist das Codebeispiel zum Testen.

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);
}
5
Zhar

Wie bereits erwähnt, müssen Sie bei JPA zwei OneToMany-Zuordnungen anstelle einer einzelnen ManyToMany-Beziehung verwenden, um die Möglichkeit zu haben, über zusätzliche Spalten zu verfügen. Sie können auch eine Spalte mit automatisch generierten Werten hinzufügen. Auf diese Weise kann es als Primärschlüssel der Tabelle verwendet werden, falls dies nützlich ist.

Zum Beispiel sollte der Implementierungscode der Extraklasse so aussehen:

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

}
2
ingitaly