I am trying to map the Student entity to several tables: students , addresses , hobbies . The keys in the addresses and hobbies are also external and refer to the primary key of the students table. That is, the table of students basic, and the rest are auxiliary. Here are the class descriptions:

Student

 package entity; import javax.persistence.*; @Entity @Table(name = "students") @SecondaryTables({ @SecondaryTable(name = "hobbies"), @SecondaryTable(name = "addresses"), }) public class Student { @Id @Column(name = "student_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column(table = "hobbies") private String hobby; @Column(table = "addresses") @Embedded private Address address; public Student(){} public Student(String name, String hobby, Address address){ this.name = name; this.hobby = hobby; this.address = address; } } 

Address

 package entity; import javax.persistence.*; @Embeddable public class Address { @Column(name="street") private String street; public Address(){} public Address(String street){ this.street = street; } } 

As a result, the String hobby field of the Student class is mapped into a separate hobbies table without problems, but the Address address cannot be displayed. That is, the addresses table is created, and the foreign key is registered in it, but the data is not received (the table is empty). In addition, there is only one field in the addresses table - student_id , and for some reason there is no street field.

It creates data like this:

 public static void main(String[] args) { StudentService service = new StudentService(); try { Student student = service.create( new Student("Вася", "Лыжи", new Address("ул. Ленина")) ); Student student2 = service.create( new Student("Петя", "Хоккей", new Address("ул. Фрунзе")) ); } catch (DBException e){ System.out.println("что-то пошло не так"); } DBService.close(); } 

This is a service layer:

 package service; import dao.*; import exception.DBException; import entity.Student; import org.hibernate.HibernateException; import org.hibernate.Transaction; import javax.persistence.NoResultException; public class StudentService { public StudentService(){} public Student create(Student student) throws DBException { Transaction transaction = DBService.getTransaction(); try { StudentDao studentDao = DaoFactory.getStudentDao(); Long studentId = studentDao.create(student); student = studentDao.get(studentId); transaction.commit(); return student; } catch (HibernateException | NoResultException | NullPointerException e) { DBService.transactionRollback(transaction); throw new DBException(e); } } } 

Why is the addresses table empty and how to make it so that it has the correct structure (it has a street field) and it is filled with data? There are no warnings or errors when executing (but there are no requests to insert data into the addresses table):

 Hibernate: drop table addresses if exists Hibernate: drop table hobbies if exists Hibernate: drop table students if exists Hibernate: create table addresses (student_id bigint not null, primary key (student_id)) Hibernate: create table hobbies (hobby varchar(255), student_id bigint not null, primary key (student_id)) Hibernate: create table students (student_id bigint generated by default as identity, street varchar(255), name varchar(255), primary key (student_id)) Hibernate: alter table addresses add constraint FKqq1nt3g94upydb2qt72xhnk7y foreign key (student_id) references students Hibernate: alter table hobbies add constraint FKmcdfc4qw94xl2a8ak5kct6rys foreign key (student_id) references students мар 15, 2019 5:43:34 PM org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@3d9f6567' Hibernate: insert into students (student_id, street, name) values (null, ?, ?) мар 15, 2019 5:43:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:./h2db] Hibernate: insert into hobbies (hobby, student_id) values (?, ?) Hibernate: select student_id from students where student_id =? for update Hibernate: insert into students (student_id, street, name) values (null, ?, ?) Hibernate: insert into hobbies (hobby, student_id) values (?, ?) Hibernate: select student_id from students where student_id =? for update Process finished with exit code 0 
  • not to do it @Embeddable , but to connect through one-to-one communication. @Embedded says that in Java code they are two different entities, but there is one table in the database. - Tsyklop
  • @Tsyklop, but why is the hobby field easily displayed in a separate table without any one-to-one? If you make a list or map display of data labeled @Embeddable , then the new table and the data in it are created without problems. How do you fix the classes in your opinion? - golubtsoff
  • one
    Remove the Column from Embedded and put @AttributeOverride (name = "street", column = @ Column (table = "addresses")) - Serodv
  • @golubtsoff the hobby field has no @Embedded annotation. try adding it and see what happens. Or as the person above suggests. - Tsyklop
  • @Serodv, thanks, buddy, it works. In this case, it @Embedded no difference @Embedded in front of the address, or not. I note that the Address class must be marked with the @Embeddable annotation, otherwise you will have to prescribe the entity in the manager configuration with all the ensuing consequences. - golubtsoff

1 answer 1

I would like to add the comment @Serodv (for which he thanks a lot), for the case when the Address class contains several fields.

 import javax.persistence.*; @Embeddable public class Address { @Column(name="street") private String street; @Column(name="building") private int building; public Address(){} public Address(String street, int building){ this.street = street; this.building = building; } } 

In this case, the Student class looks like this:

 import javax.persistence.*; @Entity @Table(name = "students") @SecondaryTables({ @SecondaryTable(name = "hobbies"), @SecondaryTable(name = "addresses"), }) public class Student { @Id @Column(name = "student_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column(table = "hobbies") private String hobby; @AttributeOverrides({ @AttributeOverride(name="street", column=@Column(table="addresses")), @AttributeOverride(name="building", column=@Column(table="addresses")) }) private Address address; public Student(){} public Student(String name, String hobby, Address address){ this.name = name; this.hobby = hobby; this.address = address; } } 

Here it is worth noting that the Address class must be marked with the @Embeddable annotation, otherwise it will need to be written in the entity manager with all the consequences (add the Id field, add @OneToOne annotation ...). You can instead put the @Embedded annotation in the Student class before the Address address field (you can put both annotations, but why clutter the code).

To display a class in a separate table containing several fields, you need to use the @AttributeOverrides annotation.

IMPORTANT!
In the @AttributeOverride(name="street", column=@Column(table="addresses")) line @AttributeOverride(name="street", column=@Column(table="addresses")) word street should be written as the field in the Address class is named, i.e. all lowercase letters. It does not matter how this field is written in the @Column(name="street") annotation @Column(name="street") in the Address class.

Similar to @AttributeOverride(name="building", column=@Column(table="addresses")) . Otherwise, in separate tables, hobbies and addresses will not have fields other than the only student_id . All fields will remain in the students table.

Apparently, this is a Hibernate bug (version 5.2.12.Final is used)