## 依賴導入 ```xml org.hibernate.orm hibernate-core 6.2.7.Final com.mysql mysql-connector-j 8.0.33 ``` ## 配置文件 ```xml com.mysql.cj.jdbc.Driver jdbc:mysql: ...
依賴導入
<!-- 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&useUnicode=true&characterEncoding=utf-8&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
: 外鍵列
- 在一對一中
@JoinColumn
聲明在那個實體類中,生成資料庫表時外鍵列就在那個類。 - 在一對多中
@JoinColumn
必須聲明在多的那個實體類中 - 在多對多中
@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欄位。