hibernate 一对一 一对多 多对多

依赖导入

<!-- hibernate 核心 -->
<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.2.7.Final</version>
</dependency>

<!--  jdbc 实现 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- 会话工厂 -->
    <session-factory>
        <!-- 链接参数 -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/jpa_study?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">a1b2c3</property>

        <!-- 显示sql语句 -->
        <property name="show_sql">false</property>
        <!-- 格式化sql -->
        <property name="format_sql">false</property>
        <!-- sql方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- 自动操作:创建-删除 -->
        <property name="hibernate.hbm2ddl.auto">create-drop</property>
        <!-- 配置sessionFactory.getCurrentSession()的上下文 -->
        <property name="current_session_context_class">thread</property>

        <!-- 导入实体类和映射关系 -->
        <mapping class="org.example.entity.User"/>
        <mapping class="org.example.entity.Address"/>
        <mapping class="org.example.entity.Vlog"/>
        <mapping class="org.example.entity.Role"/>
    </session-factory>
</hibernate-configuration>

一对一

User 实体类

@Entity
@Table(name = "user")
@Data
public class User {
  /*...省略其他内容*/
  
  // 关联列的名称:address_id
  @JoinColumn(name = "address_id")
  @OneToOne(
          // 懒加载
          fetch = FetchType.LAZY
  )
  private Address address;
}

Address 实体类

@Entity
@Setter
@Getter
@Table(name = "address")
public class Address {
  /*...省略其他内容*/

  @OneToOne(
      // 实体关联的被拥有者,只想User.address字段
      mappedBy = "address"
  )
  private User user;
}

测试

@Test
public void oneToOneTest() {
    AddressDao addressDao = new AddressDao();// Dao层已经在https://www.cnblogs.com/heirem/p/17616689.html说过了,这里就放代码了
    Address address = new Address();
    address.setDetail("广东中山");
    addressDao.save(address);

    UserDao userDao = new UserDao();
    User user = new User();
    user.setName("张三");
    user.setEmail("[email protected]");
    userDao.save(user);
    user.setAddress(address);
    userDao.update(user);

    userDao.findAll().forEach(System.out::println);
}
User(id=1, name=张三, [email protected], address=org.example.entity.Address@74bdfa0b, vLog=[], roles=[])

一对多

User实体类

@Entity
@Table(name = "user")
@Data
public class User {
  /*...省略其他内容*/

  @OneToMany(
    // 懒加载
    fetch = FetchType.LAZY,
    // 映射字段,指的是Vlog实体类中映射此实体类的字段,实体关联的被拥有者
    mappedBy = "user"
  )
  private List<Vlog> vLog;
}

Vlog实体类

@Entity
@Table(name = "vlog")
@Getter
@Setter
public class Vlog {
  /*...省略其他内容*/

  @JoinColumn(name="user_id")// join列,即外键列
  @ManyToOne(
          fetch = FetchType.LAZY
  )
  private User user;
}

测试

@Test
public void oneToManyTest() {
    UserDao userDao = new UserDao();
    User user = new User();
    user.setName("张三");
    user.setEmail("[email protected]");
    userDao.save(user);

    VlogDao vlogDao = new VlogDao();
    Vlog v1 = new Vlog();
    v1.setUrl("www.bilirubin.com/video/1");
    vlogDao.save(v1);

    Vlog v2 = new Vlog();
    v2.setUrl("www.bilirubin.com/video/2");
    vlogDao.save(v2);

    v1.setUser(user);
    v2.setUser(user);
    vlogDao.update(v1);
    vlogDao.update(v2);

    userDao.findAll().forEach(System.out::println);
    vlogDao.findAll().forEach(System.out::println);
}
User(id=1, name=张三, [email protected], address=null, vLog=[org.example.entity.Vlog@6f7a20da, org.example.entity.Vlog@77ba583], roles=[])
org.example.entity.Vlog@4cd7e993
org.example.entity.Vlog@685e6a68

多对多

@Entity
@Table(name = "user")
@Data
public class User {
  /*...省略其他内容*/

  @ManyToMany(
    // 懒加载
    fetch = FetchType.LAZY,
    // 关联操作,不过因为多对多只会影响关联表和本表的数据,即user_role表和user
    cascade = {CascadeType.ALL}
  )
  @JoinTable(
    // User Role表关联表的名称
    name = "user_role",
    // joinColumns 链接表的外键,记录此类的的id
    joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")},
    // inverseJoinColumns 链接表的外键,记录链接类的id
    inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")}
  )
  private List<Role> roles;
}
@Entity
@Table(name = "role")
@Setter
@Getter
public class Role {
  /*...省略其他内容*/

  @ManyToMany(
      fetch = FetchType.LAZY,
      // 实体关系的拥有者,即在User.roles已经声明过关联规则,这边直接关联User.roles就好了
      mappedBy = "roles"
  )
  private List<User> users;
}

测试

@Test
public void manyToManyTest() {
    RoleDao roleDao = new RoleDao();
    Role roleUser = new Role();
    roleUser.setName("user");
    roleUser.setDescription("普通用户");
    roleDao.save(roleUser);

    Role roleAdmin = new Role();
    roleAdmin.setName("admin");
    roleAdmin.setDescription("超级用户");
    roleDao.save(roleAdmin);


    UserDao userDao = new UserDao();
    User u1 = new User();
    u1.setName("张三");
    u1.setName("张三@email.com");
    userDao.save(u1);

    User u2 = new User();
    u2.setName("李四");
    u2.setName("李四@email.com");
    userDao.save(u2);

    u1.setRoles(List.of(roleUser));
    u2.setRoles(List.of(roleUser,roleAdmin));
    userDao.update(u1);
    userDao.update(u2);


    userDao.findAll().forEach(System.out::println);
}
User(id=1, name=张三@email.com, email=null, address=null, vLog=[], roles=[org.example.entity.Role@48d44b46])
User(id=2, name=李四@email.com, email=null, address=null, vLog=[], roles=[org.example.entity.Role@48d44b46, org.example.entity.Role@dd20ebc])

数据库表图解

mappedBy属性 和 JoinColumn注解

mappedby : 属性指向实体关联表的拥有者,声明在被拥有者。简单说就是另一边定义了关联规则,这边不用再定义一遍了,直接引用就行。
@JoinColumn: 外键列

  1. 在一对一中@JoinColumn 声明在那个实体类中,生成数据库表时外键列就在那个类。
  2. 在一对多中@JoinColumn 必须声明在多的那个实体类中
  3. 在多对多中@JoinColumn 必须配合@JoinTable使用

toString()造成的栈溢出StackOverflowError

hibernate中实体类如果有互相关联,比如:一对一关系中 User实体中声明了Address的字段private Address address,Address实体中声明了User的字段private User user。在序列化或者打印时,可能出现循环加载关联字段。比如打印User的实例,User的toString中又调用了Address的toString,这是去加载Address实体类的信息,加载出来后Address的toString中有引用了User的toString方法,完了就这样不断的互相调用,循环且套直至StackOverflowError
解决方法:
打印-删除掉User实体类toString引用的Address.toString的代码。
序列化-跳过User的Address字段。

热门相关:亿万盛宠只为你   变态护理人员舔病人全身   后福   不科学御兽   直播主