天天看點

Hibernate5 入門詳解

文章目錄

    • 前言
    • 環境說明
    • 配置過程
      • 1.資料庫建立
      • 2.項目搭建
      • 3.配置hibernate.cfg.xml
      • 4.映射關系
        • 4.1 xml映射方式
        • 4.2 注解映射方式
      • 5.建立SessionFactory
      • 6. 單元測試用例
    • 總結

前言

​ 為什麼突然想起寫一個hibernate的簡單配置教程呢,因為最近兩年都沒怎麼和資料庫打交道,而以前對這些架構的配置都是知其然不知其是以然,是以就想把以前簡單用過的的hibernate拿出來再熟練一下,加深印象,是以借此篇部落格,幫助他人的同時順便為以後使用留點資料,同時養成寫部落格的好習慣。

ps:後面有時間,可能就把整個測試Demo項目傳到GitHup上面去(其實代碼都給出來了)

環境說明

Mysql(windows10)5.7.26、hibernate5.3.10、druid1.1.19、c3p05.3.10

配置過程

1.資料庫建立

建立如圖所示:

Hibernate5 入門詳解

2.項目搭建

​ 此次教程基于mysql5.7.26,hibernate5.3.10,連接配接池使用了druid和c3p0(二選一),并沒有使用spring。為了導包友善,先建一個基于maven的web項目,然後在pom檔案中增加以下引用。

<properties>
		<!-- 版本管理 -->
		<hibernate.version>5.3.10.Final</hibernate.version>
		<c3p0.version>5.3.10.Final</c3p0.version>
		<mysql.version>8.0.16</mysql.version>
		<hibernate.version>5.3.10.Final</hibernate.version>
		<druid.version>1.1.19</druid.version>
		<lombok.version>1.18.8</lombok.version>

		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<!-- c3p0連接配接池 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-c3p0</artifactId>
			<version>${c3p0.version}</version>
		</dependency>
		<!-- 阿裡druid連接配接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>${druid.version}</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.version}</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
			<version>${lombok.version}</version>
		</dependency>
	</dependencies>
           

​ 先看一下工程結構

Hibernate5 入門詳解

3.配置hibernate.cfg.xml

本文主要側重于講解注解配置的方式(

推薦

),使用xml配置的方式僅提供簡單配置以供參考。

druid連接配接池的配置:

<?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>
		<!--dataSource for Druid start -->
		<property name="driverClassName">com.mysql.cj.jdbc.Driver</property>
		<property name="url">jdbc:mysql://localhost:3306/hibernatetest?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false</property>
		<property name="username">test</property>
		<property name="password">123456</property>
		<property name="filters">stat</property>
		<property name="initialSize">1</property>
		<property name="maxActive">20</property>
		<property name="minIdle">1</property>
		<property name="maxWait">60000</property>
		<property name="timeBetweenEvictionRunsMillis">60000</property>
		<property name="minEvictableIdleTimeMillis">300000</property>
		<property name="validationQuery">SELECT 1</property>
		<property name="testWhileIdle">true</property>
		<property name="testOnBorrow">false</property>
		<property name="testOnReturn">false</property>
		<property name="poolPreparedStatements">true</property>
		<property name="maxOpenPreparedStatements">20</property>
		<property name="maxPoolPreparedStatementPerConnectionSize">200</property>
     	<property name="asyncInit">true</property>
		<!--dataSource for Druid end -->
		<!--指定資料庫名,mysql低版本曾用hibernate.default_schema表示資料庫名  -->
		<property name="hibernate.default_catalog">hibernatetest</property>
		<!-- 指定資料庫方言 -->
		<!--該方言預設使用MyISAM引擎 -->
		<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<!--InnoDB -->
		<!--<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> -->

		<!-- 顯示Hibernate持久化操作所生成的SQL -->
		<property name="show_sql">true</property>

		<!-- 将SQL腳本進行格式化後再輸出 -->
		<property name="hibernate.format_sql">true</property>

		<!--自定義ConnectionProvider的類名, 此類用來向Hibernate提供JDBC連接配接. -->
		<property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property>

		<!-- 測試環境使用create/create-drop 正式環境使用update/validate -->
		<property name="hbm2ddl.auto">update</property>

		<!-- 使用JDBC連接配接檢查資料庫中繼資料,據說使用連接配接池這裡設定為false,但是整個測試過程中true或者false都挺正常,但是以防萬一還是加上吧 -->
		<property name="hibernate.temp.use_jdbc_metadata_defaults">false</property>

		<!-- 禁用二級緩存 -->
		<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

		<!-- 設定事務隔離級别 1 可讀未送出 2可讀已送出 4可重複讀 8串行化 -->
		<property name="hibernate.connection.isolation">4</property>

		<!--羅列所有持久化類的類名 -->
		<mapping class="com.hibernate.demo.entity.pojo.School" />
		<mapping class="com.hibernate.demo.entity.pojo.Teacher" />

		<!-- 用來關聯hbm配置檔案 -->
		<mapping resource="com/hibernate/demo/entity/User.hbm.xml" />
	</session-factory>
</hibernate-configuration>
           

c3p0連接配接池的配置:

<?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>
	<!--hibernate.可以省略,hibernate會做判斷,如 hibernate.c3p0.max_size可寫成c3p0.max_size -->
	<session-factory>
		<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost:3306/hibernatetest?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false</property>
		<property name="connection.username">test</property>
		<property name="connection.password">123456</property>

		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
		<!-- 指定連接配接池裡最大連接配接數 -->
		<property name="hibernate.c3p0.max_size">20</property>
		<!-- 指定連接配接池裡最小連接配接數 -->
		<property name="hibernate.c3p0.min_size">1</property>
		<!-- 指定連接配接池裡連接配接的逾時時長 -->
		<property name="hibernate.c3p0.timeout">5000</property>
		<!-- 指定連接配接池裡最大緩存多少個Statement對象 -->
		<property name="hibernate.c3p0.max_statements">100</property>
		<property name="hibernate.c3p0.idle_test_period">3000</property>
		<property name="hibernate.c3p0.acquire_increment">2</property>
		<property name="hibernate.c3p0.validate">true</property>
		
		<!--指定資料庫名,mysql低版本曾用hibernate.default_schema表示資料庫名  -->
		<property name="hibernate.default_catalog">hibernatetest</property>
		
		<!-- 指定資料庫方言 -->
		<!--該方言預設使用MyISAM引擎-->
		<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<!--InnoDB-->
		<!--<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>-->

		<!-- 顯示Hibernate持久化操作所生成的SQL -->
		<property name="show_sql">true</property>
		
		<!-- 将SQL腳本進行格式化後再輸出 -->
		<property name="hibernate.format_sql">true</property>
		
		<!-- 測試環境使用create/create-drop 正式環境使用update/validate -->
		<property name="hbm2ddl.auto">update</property>
		
		<!-- 使用JDBC連接配接檢查資料庫中繼資料,據說使用連接配接池這裡設定為false,但是整個測試過程中true或者false都挺正常,但是以防萬一還是加上吧 -->
		<property name="hibernate.temp.use_jdbc_metadata_defaults">false</property>
		
		<!-- 禁用二級緩存 -->
		<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
		
		<!-- 設定事務隔離級别  1 可讀未送出  2可讀已送出 4可重複讀  8串行化 -->
		<property name="hibernate.connection.isolation">4</property>
		
		<!--羅列所有持久化類的類名 -->
		<mapping class="com.hibernate.demo.entity.pojo.School"/>
		<mapping class="com.hibernate.demo.entity.pojo.Teacher"/>
		
		<!-- 用來關聯hbm配置檔案 -->
		<mapping resource="com/hibernate/demo/entity/User.hbm.xml" /> 
	</session-factory>
</hibernate-configuration>
           

在這裡不得不說一下方言的選擇,測試過程中建表報錯

Specified key was too long; max key length is 1000 bytes

,錯誤的原因是因為我主鍵指定長度

id varchar(300) not null

太長了,經查詢資料得知:

因為我建立資料路時指定的是

utf8mb4

,utf8mb4字元每個字元最多四個位元組,是以300*4就超出了MyISAM引擎對主鍵長度限制,是以這裡将MyISAM改為了InnoDB:

如果想要使用InnoDB引擎,有兩種方式:

方式一:

在hibernate.cfg.xml中指定方言為

org.hibernate.dialect.MySQL5InnoDBDialect

方式二:如果堅持使用

org.hibernate.dialect.MySQL5Dialect

方言,該方言預設使用MyISAM引擎,可以在src/resources/hibernate.properties 中增加以下屬性指定使用InnoDB引擎(經過測試)

hibernate.dialect.storage_engine=innodb

4.映射關系

hibernate 有兩種映射方式分别是注解(推薦)和xml,這裡我都配置一下,當然xml的僅提供一個入門級的配置,重點在注解配置方式,後面提供的單元測試用例也主要是針對注解類的。

4.1 xml映射方式

User 類

package com.hibernate.demo.entity;

import java.util.Date;
import javax.persistence.Entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
	private Integer userId;
	private String firstName;
	private String lastName;
	private String sex;
	private Date birthday;
	private String telNum;
	private Date createTime;

}
           

映射檔案必須在hibernate.cfg.xml配置才起作用,如下:

<mapping resource="com/hibernate/demo/entity/User.hbm.xml"/>

User.hbm.xml 映射檔案

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 

<!-- 注意包名。不寫的話下面要寫全限定名 -->
<hibernate-mapping    package="com.hibernate.demo.entity">
 <!-- name代表的是實體類名,table代表的是表名 -->
   <class name="User" table="user">
      <meta attribute="class-description">
         This class contains the user detail. 
      </meta>
      <id name="userId" type="int" column="user_id">
         <generator class="native"/>
         <!-- 自增長主鍵不同資料庫有不同類型的自增長類型,有需要可以百度到答案的
            <generator class="identity"></generator> -->
      </id>
        <!-- 非主鍵映射關系,注意類型并不是單純的java類型也不是資料庫類型,而是一種中間類型,注意大小寫特别是String在這裡開頭要小寫 -->
      <property name="firstName" column="first_name" type="string"/>
      <property name="lastName" column="last_name" type="string"/>
      <property name="sex" column="sex" type="string"/>
      <property name="birthday" column="birthday" type="date"/>
      <property name="telNum" column="telNum" type="string"/>
      <property name="createTime" column="createTime" type="timestamp"/>
   </class>
</hibernate-mapping>
           

4.2 注解映射方式

注解的話也是需要在hibernate.cfg.xml中指定。如下:

<mapping class="com.hibernate.demo.entity.pojo.Teacher"/>

不需要使用xml,相當的友善,下文會給出示例,實體類字段并沒什麼業務含義,單純的隻是嘗試使用不同的注解,廢話不多說直接上代碼。

BaseEntity

package com.hibernate.demo.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

@MappedSuperclass
public class BaseEntity implements java.io.Serializable {

	/**
	 * <序列号>
	 */
	private static final long serialVersionUID = 7544096556094410178L;

//  @Temporal(TemporalType.DATE)(精确到年月日)
//  @Temporal(TemporalType.TIME)(精确到時分秒)
//  @Temporal(TemporalType.TIMESTAMP)(預設年月日時分秒)

	// 記錄建立時間
	@Column(name = "created_time")
	@Temporal(TemporalType.TIMESTAMP)
	private Date createdTime;
	// 記錄更新時間
	@Column(name = "updated_time")
	@Temporal(TemporalType.TIMESTAMP)
	private Date updatedTime;
	// 描述更新内容
	@Column(name = "body")
	private String description;
	//注解用于支援樂觀鎖版本控制。
	@Version
	private Integer version;
	public Date getCreatedTime() {
		return createdTime;
	}
	public void setCreatedTime(Date createdTime) {
		this.createdTime = createdTime;
	}
	public Date getUpdatedTime() {
		return updatedTime;
	}
	public void setUpdatedTime(Date updatedTime) {
		this.updatedTime = updatedTime;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public Integer getVersion() {
		return version;
	}
	public void setVersion(Integer version) {
		this.version = version;
	}
	
}
           

School

package com.hibernate.demo.entity.pojo;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.hibernate.demo.entity.BaseEntity;

@Entity // 定義了一個實體
@Table(name = "school")
public class School extends BaseEntity{
	private static final long serialVersionUID = 1653242906575651690L;

//	TABLE:使用一個特定的資料庫表格來儲存主鍵。 
//	SEQUENCE:根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列。 
//	IDENTITY:主鍵由資料庫自動生成(主要是自動增長型) 
//	AUTO:主鍵由程式控制。
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer schoolId;

	@Column(length = 30, nullable = false)
	private String schoolName;
	
	private Long ranking; // 排名
	// 關聯關系由Teacher類維護
	// 延遲加載
	@OneToMany(mappedBy="schl", fetch=FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = Teacher.class)
	private Set<Teacher> teachers;
	public Integer getSchoolId() {
		return schoolId;
	}
	public void setSchoolId(Integer schoolId) {
		this.schoolId = schoolId;
	}
	public String getSchoolName() {
		return schoolName;
	}
	public void setSchoolName(String schoolName) {
		this.schoolName = schoolName;
	}
	public Long getRanking() {
		return ranking;
	}
	public void setRanking(Long ranking) {
		this.ranking = ranking;
	}
	public Set<Teacher> getTeachers() {
		return teachers;
	}
	public void setTeachers(Set<Teacher> teachers) {
		this.teachers = teachers;
	}
	public School() {
		super();
	}
	
	public School(Integer schoolId, String schoolName, Long ranking, Set<Teacher> teachers) {
		super();
		this.schoolId = schoolId;
		this.schoolName = schoolName;
		this.ranking = ranking;
		this.teachers = teachers;
	}
	@Override
	public String toString() {
		return "School [schoolId=" + schoolId + ", schoolName=" + schoolName + ", ranking=" + ranking + "]";
	}
	
}
           

Teacher

package com.hibernate.demo.entity.pojo;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;

import com.hibernate.demo.entity.BaseEntity;

@Entity
@Table(name = "teacher")
public class Teacher extends BaseEntity {

	private static final long serialVersionUID = -6858635556382148936L;

	// 生成UUID的主鍵生成政策
	@Id
	@GenericGenerator(name = "idGenerator", strategy = "org.hibernate.id.UUIDGenerator")
	@GeneratedValue(generator = "idGenerator")
	// 定義列名
	// insertable表示該字段是否可以出現在insert語句中,預設值為true,通常為主鍵、時間戳等字段的值設定為false。因為它們的值都是自動生成的,不需要在insert時插入;
	@Column(name = "t_id",insertable=false)
	private String teacherId;

	@Type(type = "string")
	private String name;

	@Type(type = "string")
	private String sex;

	/**
	 * fetch: 擷取政策,當以session.get()方法擷取資料庫對象時:
	 * FetchType.LAZY為懶加載,會在第一次使用該屬性(如調用getAge()方法)時才擷取。
	 * FetchType.EAGER為即時加載。
	 * optional:表示目前屬性是否可選,預設為true,如果為false,則在持久化到資料庫時,如果此屬性為null,則會失敗
	 */
	@JoinColumn(name = "s_id")
//	@ManyToOne(fetch = FetchType.EAGER, optional = true,targetEntity = School.class,cascade = CascadeType.ALL)
	@ManyToOne(fetch = FetchType.EAGER, optional = true,targetEntity = School.class,cascade = CascadeType.MERGE)
	@BatchSize(size = 50)
	private School schl;
	
	// 現在這個屬性不想生成在表中
	@Transient
	private String msg;

	public String getTeacherId() {
		return teacherId;
	}

	public void setTeacherId(String teacherId) {
		this.teacherId = teacherId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public School getSchl() {
		return schl;
	}

	public void setSchl(School schl) {
		this.schl = schl;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public Teacher() {
		super();
	}

	public Teacher(String teacherId, String name, String sex, School schl, String msg) {
		super();
		this.teacherId = teacherId;
		this.name = name;
		this.sex = sex;
		this.schl = schl;
		this.msg = msg;
	}

	/**
	 *toString 注意不要輸出學校,否則互相引用容易造成棧溢出,此處兩個類都隻輸出普通屬性
	 */
	@Override
	public String toString() {
		return "Teacher [teacherId=" + teacherId + ", name=" + name + ", sex=" + sex + ", msg=" + msg + "]";
	}

}
           

5.建立SessionFactory

package com.hibernate.demo.util;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

public class HibernateUtils {
	private static final SessionFactory sessionFactory;
	/* hibernate4.3 棄用org.hibernate.service.ServiceRegistryBuilder類 */
	static {
		try {
			/* hibernate5.X 擷取factory方式 */
			// 方式一
//			sessionFactory = new Configuration().configure().buildSessionFactory();
			// 方式二
			//  不指定配置檔案的檔案名會預設讀取hibernate.cfg.xml的内容
			// c3p0
//			StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure("hibernate.cfg_c3p0.xml").build();
			// 預設hibernate.cfg.xml   druid
			StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure().build();
			Metadata metadata = new MetadataSources(standardRegistry).getMetadataBuilder()
					.applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE).build();
			// 最後由這個metadata使用建構出sessionFactory
			sessionFactory = metadata.getSessionFactoryBuilder().build();
		/**
		 * 下面的來源于網絡,未經測試,僅供參考
		 */
			/*hibernate4.3之後版本 擷取factory方式 */
//			// 第一步 建立服務注冊對象
//			ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
//					.applySettings(configuration.getProperties()).build();
//			// 第二步 建立會話工廠對象
//			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		}catch (Throwable ex) {
			ex.printStackTrace();
			// Log exception!
			throw new ExceptionInInitializerError(ex);
		}
	}

	public static Session getSession() throws HibernateException {
		return sessionFactory.openSession();
	}
}
           

6. 單元測試用例

package com.hibernate.demo.util;

import static org.junit.Assert.assertNotNull;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Root;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import com.hibernate.demo.entity.pojo.School;
import com.hibernate.demo.entity.pojo.Teacher;

class HibernateUtilsTest {
	private static Session session;
	private static SimpleDateFormat sdf;

	@BeforeAll
	static void setUpBeforeClass() throws Exception {
		sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		session = HibernateUtils.getSession();
	}

	@AfterAll
	static void tearDownAfterClass() throws Exception {
		if(session!=null) {
			session.close();
		}
	}

	@BeforeEach
	void setUp() throws Exception {
	}

	@AfterEach
	void tearDown() throws Exception {
	}

	/**
	 * 先增加學校再增加老師
	 * @throws Exception 
	 */
	@Test
	void testSave1() throws Exception {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			School scl1 = new School(null, "黃岡中學", 233L,null);
			scl1.setCreatedTime(sdf.parse("1904-12-31 00:00:00"));
			scl1.setUpdatedTime(new Date());
			scl1.setDescription("黃岡中學的描述");
			
			School scl2 = new School(null, "華師一附中", 220L,null);
			scl2.setCreatedTime(sdf.parse("1950-9-21 00:00:00"));
			scl2.setUpdatedTime(new Date());
			scl2.setDescription("華師一附中的描述");
			
			Integer id1 = (Integer)session.save(scl1);
			Integer id2 = (Integer)session.save(scl2);
			
			Teacher t1 = new Teacher(null, "張三", "男", scl1, "張三是一名體育老師");
			t1.setCreatedTime(new Date());
			t1.setUpdatedTime(new Date());
			t1.setDescription("張三老師的描述");
			session.save(t1);
			
			Teacher t2 = new Teacher("", "李四", "女", scl1, "李四是一名音樂老師");
			t2.setCreatedTime(new Date());
			t2.setUpdatedTime(new Date());
			t2.setDescription("李四老師的描述");
			session.save(t2);
			
			Teacher t3 = new Teacher("", "王五", "男", scl2, "王五是一名化學老師");
			t3.setCreatedTime(new Date());
			t3.setUpdatedTime(new Date());
			t3.setDescription("王五老師的描述");
			session.save(t3);
			
			tx.commit();
			System.out.println("黃岡中學記錄主鍵:"+id1+"\r\n華師一附中記錄主鍵:"+id2);
		} catch (HibernateException e) {
			if (tx != null){
				tx.rollback();
			}
			e.printStackTrace();
		}
	}
	/**
	 * 先增加老師再增加學校
	 */
	@Test
	void testSave2() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			School scl = new School(null, "XX高中", 1L,null);
			scl.setCreatedTime(new Date());
			scl.setCreatedTime(new Date());
			scl.setUpdatedTime(new Date());
			scl.setDescription("XX高中的描述");
			// 關聯關系由Teacher維護,設定teacher類cascade = CascadeType.ALL可避免scl對象是瞬态無法儲存錯誤
			// 或者session.persist(scl);
			session.persist(scl);
			Teacher t1 = new Teacher("", "趙六", "未知", scl, "趙六老師的備注資訊");
			t1.setCreatedTime(new Date());
			t1.setUpdatedTime(new Date());
			t1.setDescription("趙六老師的描述");
			session.save(t1);
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null){
				tx.rollback();
			}
			e.printStackTrace();
		}
	}

	
	@Test
	public void deleteTeacher() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			String hql = "from Teacher t where t.name = ?1";
			Query<?> query = session.createQuery(hql);
			query.setParameter(1,"趙六");
			@SuppressWarnings("unchecked")
			List<Teacher> list = (List<Teacher>) query.list();
	        for (Teacher tec : list) {
	        	String teacherId = tec.getTeacherId();
	        	Integer schoolId = tec.getSchl().getSchoolId();
	        	assertNotNull(teacherId);
	        	session.delete(tec);
	        	// 如果Teacher類設定為 CascadeType.ALL  檢測到學校是會被級聯删除,是以這裡設定CascadeType.MERGE
	        	School school = session.get(School.class, schoolId);
	        	System.out.println(school);
	        }
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			e.printStackTrace();
		}
	}
	
	@Test
	public void findAll() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			List<?> employees = session.createQuery("FROM School").list();
			for (Iterator<?> iterator = employees.iterator(); iterator.hasNext();) {
				School sc = (School) iterator.next();
				System.out.print("學校名稱: " + sc.getSchoolName());
				// 因為是延遲加載,執行下面一行時才會查詢teacher資訊
				// 一般一對多在一方設定fetch = FetchType.LAZY可提高效率
				Set<Teacher> teachers = sc.getTeachers();
				if(teachers == null) {
					System.out.println(sc);
				}
				System.out.println("學校老師總數:"+teachers.size());
			}
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null){
				tx.rollback();
			}
			e.printStackTrace();
		}
	}
 
	@Test
	public void queryByHQL() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			String hql = "from School s order by s.ranking desc";
			Query<School> query = session.createQuery(hql, School.class);
			//指定從那個對象開始查詢,參數的索引位置是從0開始的,
			query.setFirstResult(0);
			//分頁時,一次最多産尋的對象數
			query.setMaxResults(1);
			List<School> list = query.getResultList();
			for (School s : list) {
				System.out.println(s.getSchoolName());
			}
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			e.printStackTrace();
		}
	}
	@Test
	public void queryByQBC() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();

			CriteriaBuilder builder = session.getCriteriaBuilder();
			CriteriaQuery<School> criteria = builder.createQuery(School.class);
			Root<School> from = criteria.from(School.class);
			Order order = builder.desc(from.get("ranking"));
            criteria.orderBy(order);
            Query<School> query = session.createQuery(criteria); 
			//指定從那個對象開始查詢,參數的索引位置是從0開始的,
			query.setFirstResult(0);
			//分頁時,一次最多産尋的對象數
			query.setMaxResults(1);
			List<School> list = query.getResultList();
			for (School s : list) {
				System.out.println(s.getSchoolName());
			}
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			e.printStackTrace();
		}
	}

	
	@Test
	public void deleteSchool() {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			School school = session.get(School.class, 5);
			if(school!=null) {
				session.delete(school);
			}
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			e.printStackTrace();
		}
	
	}
}
           

總結

​因為用的hibernate版本較新,而且過程中也嘗試了一些不同的設定,總之各種折騰。網上相關資料少而且無法解決問題,最終還是一步步debug,不斷嘗試,才能完成這篇部落格。總的來說還是很有收獲,畢竟以前很多東西連一知半解都算不上,但是現在整個環境搭建測試一遍,對整個hibernate的了解還是加深了很多,第一次用markdown格式寫這種正式部落格,可能排版不盡人意🐸,如果文中有不正确的地方,希望大家不吝指教😘