[JAVA] Référence mutuelle de l'entité de Spring Data JPA et ses notes

Spring Data JPA, un framework pour la persistance des données dans Spring, vous permet de définir des relations entre deux classes Entity. À ce moment, vous pouvez définir une référence à l'autre objet dans la classe de l'autre, qui est appelée une référence mutuelle. Il y avait plusieurs points bloqués lors de l'utilisation de références mutuelles, je vais donc les résumer. La version que j'ai utilisée est Spring Data JPA 1.10.6 et je crée un projet à l'aide de Spring Boot 1.4.3.

Référence mutuelle entre les classes d'entité

Les relations entre les classes Entity peuvent être spécifiées à l'aide d'annotations telles que "@OneToOne", "@OneToMany", "@ManyToOne" et "@ManyToMany". Les relations exprimées par "@OneToMany" et "@ManyToOne" semblent être les mêmes, mais vous pouvez spécifier comment la classe Entity de l'autre côté est liée à la classe Entity.

Plus précisément, regardons la relation entre les données des employés et de l'entreprise. Le diagramme ER est le suivant.

image

Si cela est transformé en une classe d'entité de référence mutuelle, ce sera comme suit.

Company.java


@Entity
class Company {
    @Id
    private long id;

    private String name;

    @OneToMany //Plusieurs à 1
    List<Employee> employees;

    /*Abréviation d'accesseur*/
}

Employee.java


@Entity
class Employee {
    @Id
    private long id;

    private String name;

    @ManyToOne //Beaucoup
    Company company;
    
    /*Abréviation d'accesseur*/
}

Puisqu'il s'agit d'une référence mutuelle, l'entreprise dispose d'une liste de classes d'employés, qui ne figure pas sur le diagramme ER. L'ID de société côté employé devient une référence à la classe de société lors de sa classification.

Ordre d'enregistrement dans DB

Comme indiqué dans le diagramme ER, l'ID de l'entreprise (référence à la classe de l'entreprise) dans l'entité employé est traité comme une clé externe. En général SGBDR, si une clé externe est définie, l'élément cible doit exister en premier. Il en va de même pour JPA, où l'objet société doit d'abord être enregistré avant que l'objet employé puisse être enregistré dans le DB.

@Autowired
EmployeeRepository empRepository;
@Autowired
CompanyRepository coRepository;

@PostConstruct
public init() {
    /*Enregistrement de la société*/
    Company co = new Company();
    co.setName("Société A");
    coRepository.saveAndFlush(co);

    /*Inscription des employés*/
    Employee emp = new Employee();
    emp.setName("Sato");
    emp.setCompany(co); //Paramètres d'entreprise enregistrés requis
    empRepository.saveAndFlush(emp);
}

Stockage de données

Par le processus ci-dessus, nous avons pu enregistrer la société «co» et l'employé «emp» dans la base de données. Cependant, lorsque je retire «co» du «coRepository» et que je regarde le contenu des «employés», les données de M. Sato ne sont pas stockées. En fait, même si vous créez une entité de référence mutuelle, les informations de l'objet correspondant ne sont pas automatiquement stockées dans la structure de données correspondante. Par conséquent, pour faire référence à l'objet de M. Sato à partir de l'objet de la société A, il est nécessaire de définir explicitement les données et de les enregistrer dans la base de données.

public init() {
    /*Enregistrement de la société*/
    /*Inscription des employés*/
    co.setEmployees(Arrays.asList(emp));
    coRepository.saveAndFlush(co);
}

Pour être enregistré, l'objet employé cible doit d'abord être enregistré dans le DB. La raison pour laquelle cela se produit est que les références d'employé à entreprise ont des données dans la table des employés, tandis que les références d'entreprise à employé gèrent les relations de référence dans une table séparée. Parce que c'est. En d'autres termes, les deux entités de référence mutuelle sont gérées par les trois tableaux suivants.

image

Référencé par mappedBy

Cependant, cette méthode gère séparément les informations «entreprise pour salariés» et «salarié pour entreprise». Par conséquent, utilisez l'option mappedBy fournie par @OneToMany.

Employee.java


/*réduction*/
    @OneToMany(mappedBy="company") //Plusieurs à 1
    List<Employee> employees;
/*réduction*/

La valeur spécifiée pour mappedBy sera le "nom de variable de champ correspondant (avec @ManyToOne)".

Compnay.java


/*réduction*/
    @ManyToOne //Beaucoup
    Company company;
/*réduction*/

Cela empêchera la création de la table de gestion des références. S'il n'y a pas de table de gestion de référence, le contenu des «employés» est automatiquement créé à partir de la table des employés. Cela élimine le besoin de définir une liste de «Employés» avec la méthode «setEmployees» de «Company».

Référence circulaire

Les références mutuelles sont des références circulaires car elles ont toutes deux des références d'objet l'une à l'autre. Lorsque vous traitez avec des objets de référence circulaires, vous devez être conscient de la possibilité d'étendre la référence indéfiniment. Lors de la conversion en un objet JavaScript avec RestController ou Thyemelaf of Spring, le processus de conversion de chaque objet au format json est effectué. Cependant, si vous utilisez un objet qui a des références circulaires, il se développera indéfiniment. Par exemple, si vous développez l'objet Employé:

{"id":1, "name":"Sato", "company":{"id":1, "name":"Société A", "employees":[{"id":1, "name":"Sato", "company":{…}}]}}

Cela entraînera finalement une StackOverflowError et le programme plantera.

Évitez l'expansion infinie des références circulaires

Une façon d'éviter une expansion infinie est, bien sûr, d'arrêter les références circulaires (références mutuelles). Vous pouvez éviter une expansion infinie en supprimant le champ company de la classe Employee ou le champ ʻemployeesde la classe Company pour éliminer les références circulaires. Cependant, comme les références mutuelles sont pratiques, il y a des moments où vous souhaitez éviter une expansion infinie tout en laissant des références mutuelles. Pour ce faire, percez un trou dans le processus d'expansion des objets de Spring. Le processus d'expansion d'objet étend le getter cible, c'est-à-dire la méthode nomméeget ○○. Par conséquent, vous pouvez éviter une expansion infinie en renommant les getters des objets référencés. Par exemple, renommez la méthode getEmployee de la classe Company en ʻacquireEmployee. Ensuite, la méthode ʻacquireEmployee` n'est pas incluse dans la cible d'expansion de jsonization, vous pouvez donc éviter une expansion infinie.

Company.java


@Entity
class Company {
    /*réduction*/

    /*Changer en privé*/
    private getEmployees(){
        return employees;
    }

    /*Appel getter*/
    public acquireEmpployees(){
        return getEmployees();
    }
}

Dans l'exemple ci-dessus, au lieu de simplement le renommer, j'ai rendu le getter d'origine privé et l'ai appelé depuis le public ʻacquireEmployees`. Même les méthodes avec le préfixe get ne développent pas les méthodes privées, évitant ainsi les références circulaires.

{"id":1, "name":"Sato", "company":[{"id":1, "name":"Société A"}]}

Vous ne pourrez pas voir les employés de l'entreprise dans RestController ou les objets JavaScript. Cependant, il peut être utilisé en Java et Thymeleaf en appelant la méthode ʻacquireEmployees`.

thymeleaf


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

Bonus: à propos du développement de jsonization

Comme mentionné ci-dessus, le processus de jsonisation appelle une méthode qui est publique et a get comme préfixe pour effectuer l'expansion. Inversement, si la méthode est publique et a get comme préfixe, elle sera incluse dans la propriété de l'objet au moment de la jsonisation même si elle n'a pas les informations renvoyées par elle dans le champ. Par exemple, si vous souhaitez que la propriété inclue le nombre d'employés dans l'entreprise, ajoutez la méthode getEmployeeNumber.

Company.java


@Entity
class Company {
    /*réduction*/

    private getEmployees(){
        return employees;
    }

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

En utilisant cela, même si json ne peut pas inclure un objet qui provoque une référence circulaire, il est possible de renvoyer des valeurs représentatives de données. Vous pouvez également renvoyer complètement chaque élément de données en définissant une classe interne qui exclut les références (bien que ce soit fastidieux).

Page de référence

StackOverFlow lors de l'appel de l'entité référencée mutuellement par @OneToMany en JavaScript à l'aide de Thymeleaf Relations JPA: @OneToMany et @ManyToOne Premier JPA - Simple et facile à utiliser, découvrez les bases de la fonction de persistance des données de Java EE

Recommended Posts

Référence mutuelle de l'entité de Spring Data JPA et ses notes
Jusqu'à l'utilisation de Spring Data et JPA Part 2
Jusqu'à l'utilisation de Spring Data et JPA Part 1
[Comment installer Spring Data Jpa]
Sortie du journal Spring Data JPA SQL
Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA
Créer une API REST avec Spring JPA Data avec REST et Lombok incroyablement facile.
OU rechercher avec la spécification Spring Data Jpa
Spring avec Kotorin --2 RestController et Data Class
Existe en utilisant la spécification dans Spring Data JPA
Méthode d'implémentation pour source multi-données avec Spring boot (Mybatis et Spring Data JPA)
Spring Data JPA save select-insert n'est qu'une insertion
Trier par Spring Data JPA (avec tri par clé composée)
Création d'un référentiel commun avec Spring Data JPA
Spring Boot + Spring Data JPA À propos des jointures de table multiples
Vérifiez le comportement de getOne, findById et des méthodes de requête avec Spring Boot + Spring Data JPA
Test des entités et référentiels JPA à l'aide de Spring Boot @DataJpaTest
Un mémorandum lors de l'essai de Spring Data JPA avec STS
J'ai essayé de démarrer avec Spring Data JPA
Créer la variable de clause where dans Spring Data JPA
[Spring Data JPA] La condition And peut-elle être utilisée dans la méthode de suppression implémentée automatiquement?