[JAVA] Gegenseitige Bezugnahme auf Entity of Spring Data JPA und seine Anmerkungen

Mit Spring Data JPA, einem Framework zum Speichern von Daten in Spring, können Sie Beziehungen zwischen zwei Entitätsklassen definieren. Zu diesem Zeitpunkt können Sie einen Verweis auf das andere Objekt in der Klasse des jeweils anderen definieren, der als gegenseitiger Verweis bezeichnet wird. Es gab eine Menge Dinge, die bei der Verwendung gegenseitiger Referenzen hängen blieben, deshalb werde ich sie zusammenfassen. Die von mir verwendete Version ist Spring Data JPA 1.10.6 und ich erstelle ein Projekt mit Spring Boot 1.4.3.

Gegenseitige Bezugnahme zwischen Entitätsklassen

Beziehungen zwischen Entitätsklassen können mithilfe von Anmerkungen wie "@OneToOne", "@OneToMany", "@ManyToOne" und "@ManyToMany" angegeben werden. Die durch "@OneToMany" und "@ManyToOne" ausgedrückten Beziehungen scheinen identisch zu sein, Sie können jedoch angeben, wie die Beziehung zwischen der Entitätsklasse auf der anderen Seite aus der Sicht der Entitätsklasse ist.

Betrachten wir insbesondere die Beziehung zwischen Mitarbeiter- und Unternehmensdaten. Das ER-Diagramm ist wie folgt.

image

Wenn dies zu einer Entity-Klasse mit gegenseitiger Referenz gemacht wird, ist dies wie folgt.

Company.java


@Entity
class Company {
    @Id
    private long id;

    private String name;

    @OneToMany //Viele zu 1
    List<Employee> employees;

    /*Accessor-Abkürzung*/
}

Employee.java


@Entity
class Employee {
    @Id
    private long id;

    private String name;

    @ManyToOne //Viele
    Company company;
    
    /*Accessor-Abkürzung*/
}

Da es sich um eine gegenseitige Referenz handelt, verfügt das Unternehmen über eine Liste von Mitarbeiterklassen, die nicht im ER-Diagramm enthalten ist. Die Firmen-ID auf der Mitarbeiterseite wird zu einem Verweis auf die Firmenklasse, wenn sie klassifiziert wird.

Registrierungsauftrag in DB

Wie im ER-Diagramm gezeigt, wird die Unternehmens-ID (Verweis auf die Unternehmensklasse) in der Mitarbeiterentität als externer Schlüssel behandelt. Im Allgemeinen muss das Zielelement zuerst vorhanden sein, wenn ein externer Schlüssel festgelegt ist. Gleiches gilt für JPA, bei dem das Firmenobjekt erst registriert werden muss, bevor das Mitarbeiterobjekt in der DB registriert werden kann.

@Autowired
EmployeeRepository empRepository;
@Autowired
CompanyRepository coRepository;

@PostConstruct
public init() {
    /*Firmenregistrierung*/
    Company co = new Company();
    co.setName("Firma A.");
    coRepository.saveAndFlush(co);

    /*Mitarbeiterregistrierung*/
    Employee emp = new Employee();
    emp.setName("Sato");
    emp.setCompany(co); //Registrierte Firmeneinstellungen erforderlich
    empRepository.saveAndFlush(emp);
}

Datenspeicher

Durch den oben genannten Prozess konnten wir die Firma "co" und die Mitarbeiter "emp" in der DB registrieren. Wenn ich jedoch "co" aus "coRepository" herausnehme und den Inhalt von "Mitarbeitern" betrachte, werden die Daten von Herrn Sato nicht gespeichert. Selbst wenn Sie eine gegenseitige Referenzentität erstellen, werden die Informationen des entsprechenden Objekts nicht automatisch in der entsprechenden Datenstruktur gespeichert. Um auf das Objekt von Herrn Sato aus dem Objekt von Firma A zu verweisen, ist es daher erforderlich, die Daten explizit festzulegen und in der Datenbank zu speichern.

public init() {
    /*Firmenregistrierung*/
    /*Mitarbeiterregistrierung*/
    co.setEmployees(Arrays.asList(emp));
    coRepository.saveAndFlush(co);
}

Um sich zu registrieren, muss das Ziel-Mitarbeiterobjekt zuerst in der Datenbank registriert werden. Der Grund dafür ist, dass Mitarbeiter-zu-Unternehmen-Referenzen Daten in der Mitarbeitertabelle enthalten, während Unternehmen-zu-Mitarbeiter-Referenzen Überweisungsbeziehungen in einer separaten Tabelle verwalten. Denn es ist. Mit anderen Worten, die beiden Einheiten der gegenseitigen Bezugnahme werden durch die folgenden drei Tabellen verwaltet.

image

Referenziert von mappedBy

Diese Methode verwaltet jedoch die Informationen "Unternehmen für Mitarbeiter" und "Mitarbeiter für Unternehmen" getrennt. Verwenden Sie daher die von @OneToMany bereitgestellte Option "mappedBy".

Employee.java


/*Kürzung*/
    @OneToMany(mappedBy="company") //Viele zu 1
    List<Employee> employees;
/*Kürzung*/

Der für mappedBy angegebene Wert ist der "entsprechende Feldvariablenname (mit @ManyToOne)".

Compnay.java


/*Kürzung*/
    @ManyToOne //Viele
    Company company;
/*Kürzung*/

Dadurch wird verhindert, dass die Referenzverwaltungstabelle erstellt wird. Wenn keine Referenzverwaltungstabelle vorhanden ist, wird der Inhalt von "Mitarbeitern" automatisch aus der Mitarbeitertabelle erstellt. Dadurch entfällt die Notwendigkeit, eine Liste von "Mitarbeitern" mit der "setEmployees" -Methode von "Unternehmen" festzulegen.

Zirkulärer Verweis

Gegenseitige Referenzen sind Zirkelreferenzen, da beide Objektreferenzen zueinander haben. Wenn Sie mit kreisförmigen Referenzobjekten arbeiten, müssen Sie sich der Möglichkeit bewusst sein, die Referenz auf unbestimmte Zeit zu erweitern. Bei der Konvertierung in ein JavaScript-Objekt mit RestController oder Thyemelaf of Spring wird die Konvertierung jedes Objekts in das JSON-Format durchgeführt. Wenn Sie jedoch ein Objekt mit einem Zirkelverweis jsonen, wird es unbegrenzt erweitert. Wenn Sie beispielsweise das Employee-Objekt erweitern:

{"id":1, "name":"Sato", "company":{"id":1, "name":"Firma A.", "employees":[{"id":1, "name":"Sato", "company":{…}}]}}

Dies führt schließlich zu einem StackOverflowError und das Programm stürzt ab.

Vermeiden Sie die unendliche Erweiterung von Zirkelverweisen

Eine Möglichkeit, eine unendliche Expansion zu vermeiden, besteht natürlich darin, Zirkelverweise (gegenseitige Verweise) zu stoppen. Sie können eine unendliche Erweiterung vermeiden, indem Sie das Feld "Unternehmen" der Klasse "Mitarbeiter" oder das Feld "Mitarbeiter" der Klasse "Unternehmen" entfernen, um Zirkelverweise zu entfernen. Da gegenseitige Verweise jedoch praktisch sind, möchten Sie manchmal eine unendliche Erweiterung vermeiden, während Sie gegenseitige Verweise belassen. Stecken Sie dazu ein Loch in den Objekterweiterungsprozess von Spring. Der Objekterweiterungsprozess erweitert den Ziel-Getter, dh die Methode mit dem Namen "get ○○". Daher können Sie eine unendliche Erweiterung vermeiden, indem Sie die Getter von Objekten umbenennen, auf die verwiesen wird. Benennen Sie beispielsweise die Methode "getEmployee" der Company-Klasse in "purchaseEmployee" um. Dann wird die Methode "purchaseEmployee" nicht in das Erweiterungsziel von jsonization aufgenommen, sodass Sie eine unendliche Erweiterung vermeiden können.

Company.java


@Entity
class Company {
    /*Kürzung*/

    /*Wechseln Sie zu privat*/
    private getEmployees(){
        return employees;
    }

    /*Rufen Sie Getter an*/
    public acquireEmpployees(){
        return getEmployees();
    }
}

Im obigen Beispiel habe ich den ursprünglichen Getter nicht einfach umbenannt, sondern privat gemacht und ihn von den öffentlichen "AcquirEmployees" genannt. Selbst Methoden mit dem Präfix get erweitern private Methoden nicht und vermeiden Zirkelverweise.

{"id":1, "name":"Sato", "company":[{"id":1, "name":"Firma A."}]}

RestController- und JavaScript-Objekte können Mitarbeiter im Unternehmen nicht sehen. Es kann jedoch in Java und Thymeleaf verwendet werden, indem die Methode "purchaseEmployees" aufgerufen wird.

thymeleaf


<ul>
    <li th:each="emp: ${company.acquireEmployees()}" th:text="${emp}"></li>
</ul>

Bonus: Über die Entwicklung der Jsonisierung

Wie oben erwähnt, ruft der jsonization-Prozess eine Methode auf, die öffentlich ist und als Präfix für die Durchführung der Erweiterung verwendet wird. Wenn die Methode dagegen öffentlich ist und als Präfix erhalten wurde, wird sie zum Zeitpunkt der Jsonisierung in die Eigenschaft des Objekts aufgenommen, auch wenn die von ihr zurückgegebenen Informationen nicht im Feld vorhanden sind. Wenn Sie beispielsweise möchten, dass die Eigenschaft die Anzahl der Mitarbeiter im Unternehmen enthält, fügen Sie die Methode "getEmployeeNumber" hinzu.

Company.java


@Entity
class Company {
    /*Kürzung*/

    private getEmployees(){
        return employees;
    }

    public getEmployeeNumber(){
        return getEmployees().size();
    }
}
{"id":1, "name":"Sato", "company":[{"id":1, "name":"Firma A.", "employeeNumber":1}]}

Auf diese Weise können repräsentative Datenwerte zurückgegeben werden, auch wenn json kein Objekt einschließen kann, das einen Zirkelverweis verursacht. Sie können jedes Datenelement auch vollständig zurückgeben, indem Sie eine innere Klasse definieren, die Referenzen ausschließt (obwohl dies langwierig ist).

Referenzseite

StackOverFlow beim Aufrufen einer Entität, auf die @OneToMany in JavaScript mit Thymeleaf gegenseitig verweist JPA-Beziehungen: @OneToMany und @ManyToOne Erste JPA - Einfach und benutzerfreundlich, lernen Sie die Grundlagen der Datenpersistenzfunktion von Java EE kennen

Recommended Posts

Gegenseitige Bezugnahme auf Entity of Spring Data JPA und seine Anmerkungen
Bis zur Verwendung von Spring Data und JPA Part 2
Bis zur Verwendung von Spring Data und JPA Part 1
[So installieren Sie Spring Data Jpa]
Spring Data JPA SQL-Protokollausgabe
Sehen Sie sich das Verhalten von Entitätsaktualisierungen mit Spring Boot + Spring Data JPA an
Das Erstellen einer REST-API mit Spring JPA-Daten mit REST und Lombok ist unglaublich einfach.
ODER suchen Sie mit der Spring Data Jpa-Spezifikation
Feder mit Kotorin --2 RestController und Datenklasse
Existiert mit der Spezifikation in Spring Data JPA
Implementierungsmethode für Multi-Datenquelle mit Spring Boot (Mybatis und Spring Data JPA)
Spring Data JPA Save Select-Insert ist nur Insert
Sortieren nach Spring Data JPA (mit zusammengesetzter Schlüsselsortierung)
Erstellen eines gemeinsamen Repositorys mit Spring Data JPA
Spring Boot + Spring Data JPA Informationen zu mehreren Tabellenverknüpfungen
Überprüfen Sie das Verhalten von getOne-, findById- und Abfragemethoden mit Spring Boot + Spring Data JPA
Testen von JPA-Entitäten und -Repositorys mit Spring Boot @DataJpaTest
Ein Memorandum beim Versuch von Spring Data JPA mit STS
Ich habe versucht, mit Spring Data JPA zu beginnen
Machen Sie die where-Klauselvariable in Spring Data JPA
[Spring Data JPA] Kann die And-Bedingung in der automatisch implementierten Löschmethode verwendet werden?