超基础的hibernate入门笔记

Hibernate简介

(度娘解释)

  • Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

  • 什么意思呢?其实,直白点说就是,hibernate框架会把你创建的Java实体类对象自动映射成数据库表,该框架可以自动建表,进行crud操作,程序员不必亲自操作数据库。

如下:将一个User实体类映射成t_user数据库表。

在这里插入图片描述

什么是ORM?

  • 对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。直白说就是一种将实体类映射成对应的数据库表格的设计思想。

Hibernate怎么开始呢?

一、进入官网

Hibernate官网

二、点击orm

在这里插入图片描述

三、点击Documentation,选择版本

在这里插入图片描述

四、Getting Started Guide

在这里插入图片描述

五、慢慢看吧。。。

在这里插入图片描述

Hibernate“三步走”

我使用的环境是:JDK11 + MySQL8.0 + Hibernate5.4.18

导入jar包

这里可以选择将releases of Hibernate下载到本地再导入jar包,也可以使用maven项目直接引入。为了学习方便,我选择下载!

  • 解压后的lib目录下,有一个required文件夹
    在这里插入图片描述
  • 创建一个普通的Java项目,将该目录下的jar包全部导入
  • 此外,还需要导入MySQL的jdbc驱动包
  • 最后,Bulid Path一下就哦了!
创建实体类
  • 创建实体类时,需要注意实体类中必须有一个唯一标识该实体的字段,比如:id,会作为数据库表的id主键约束。

public class User {
	private int id;
	private String username;
	private String password;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}
编写配置文件
①编写配置实体类和数据库表的映射关系的文件
  • 配置文件要求
名称位置后缀建议
无要求无要求.hbm.xml位置:实体类所在包;名称:实体类名.hbm.xml
  • 配置文件写法
步骤目标备注
1引入xml约束hibernate-core-5.4.18.Final.jar包下的org.hibernate包中,给出了相应的xml配置文件,从中可以找到对应的xml约束,找mapping类型的即可,我这里使用的时dtd约束。如果引入后没有提示,可以下载下来引入本地dtd!
2配置类和表的映射使用<class>标签,name:实体类全限定名称,table:数据库表明
3配置实体类id和表id字段对应主键生成策略:<generator class="native"></generator>,native: 根据使用的数据库自动选择主键生成策略<id>column属性可以省略,省略时默认和name一致
4配置其他属性和表的字段对应<property/>标签中,column属性可以省略,省略时默认和name一致
  • 完成后的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<!-- 配置类和表的映射
		name: 实体类的全限定名称
		table: 数据库表名
	 -->
	<class name="demo.test.pojo.User" table="t_user">
		<!-- 配置实体类id和数据库表id对应
			name: 实体类属性名
			column: 数据库表id名
		 -->
		<id name="id" column="id">
			<!-- 设置数据库id增长策略
				native: 根据使用的数据库自动选择主键生成策略
			 -->
			<generator class="native"></generator>
		</id>
		
		<!-- 配置其他属性和数据库列名映射关系 -->
		<property name="username" column="username"></property>
		<property name="password" column="password"></property>
	</class>
</hibernate-mapping>
②编写hibernate核心配置文件
  • 配置文件要求
名称位置后缀
必须为hibernate.cfg.xml必须在src.cfg.xml
  • 配置文件写法

这个配置文件中所有要配置的内容,在hibernate-release-5.4.18.Final\project\etc中的hibernate.properties可以找到。

  • 要注意,需要提前创建好数据库,hibernate可以自动创建表,但是不能自动创建数据库!!!而且配置内容要在<session-factory>标签内!!!
步骤目标备注
1引入xml约束hibernate-core-5.4.18.Final.jar包下的org.hibernate包中,给出了相应的xml配置文件,从中可以找到对应的xml约束,找configuration类型的即可,我这里使用的时dtd约束。如果引入后没有提示,可以下载下来引入本地dtd!
2配置数据库信息注意MySQL8.0和之前版本的Driver路径不一样哦!
3配置hibernate信息可选,应当开启hibernate的自动创建表功能hibernate.hbm2ddl.auto,值为update:无表创建有表更新!不开启是不能自动创建表的哦!
4配置映射文件路径hibernate只会加载核心配置文件,不会加载其他映射文件,所以需要配置。友情提示:配置路径时,eclipse中按住Ctrl点击路径,如果正常跳转,说明配置成功!
  • 完成后的配置文件
<?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/hibernate</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">leidada</property>
		
		<!-- 第二部分:配置hibernate信息 -->
		<!-- 打印sql语句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 对sql语句进行格式化 -->
		<property name="hibernate.format_sql">true</property>
		<!-- 开启表的自动更新
			update: 无表创建,有表更新
		 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 如果使用分页,需要配置数据库方言,使其能够识别各自的语言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- 第三部分:配置映射文件 -->
		<mapping resource="demo/test/pojo/User.hbm.xml"/>
	</session-factory>
</hibernate-configuration>
  • 如果报错
WARN: GenerationTarget encountered exception accepting command : Error executing DDL "(。。。)
  • 修改为以下内容
    这里主要是因为数据库版本问题!!!
<!-- 如果使用分页,需要配置数据库方言,使其能够识别各自的语言 
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>-->
③至此,配置完毕,测试添加操作
  • 步骤
步骤
1:加载核心配置文件
2:创建SessionFactory对象,这一步会创建表
3:创建Session对象,类似数据库连接中的Connection对象
4:开启事务
5:具体逻辑,crud操作
6:提交事务
7:关闭资源
  • 测试代码(注意别导错包哦!)
public class DemoTest {

	public static void main(String[] args) {
		// 加载核心配置文件
		Configuration cfg = new Configuration();
		cfg.configure();
		// 创建SessionFactory对象
		SessionFactory factory = cfg.buildSessionFactory();
		// 创建Session对象
		Session session = factory.openSession();
		// 开启事务
		Transaction tx = session.beginTransaction();
		// 具体逻辑,添加操作
		User user = new User();
		user.setUsername("张三");
		user.setPassword("123456");
		session.save(user);
		// 提交事务
		tx.commit();
		// 关闭资源
		session.close();
		factory.close();
	}

}

  • 效果

Ⅰ、数据库被创建
在这里插入图片描述

Ⅱ、数据被添加
在这里插入图片描述

Ⅲ、目录结构(别在意小红×,注释标签引起的。。。)
在这里插入图片描述



Hibernate主键生成策略

生成策略作用
increment用于long、short、int类型,由Hibernate自动以递增的方式生成唯一标识符,每次增量为1。只有当没有其它进程向同一张表中插入数据时才可以使用,不能在集群环境下使用。适用于代理主键。
identity采用底层数据库本身提供的主键生成标识符,条件是数据库支持自动增长类型数据。在DB2、MySQL、MS SQL Server、Sybase和HypersionicSQL数据库中可以是使用该生成器,适用于代理主键。
sequenceHibernate根据底层数据库序列生成标识符,条件是数据库支持序列。适用于代理主键。
native根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo三种生成器中的一种,适合跨数据库平台开发。适用于代理主键。
uuidHibernate采用128位的UUID算法来生成标识符。能够在网络环境中生成唯一的字符串标识符,其UUID被编码为一个长度为32位的十六进制字符串。占用空间大,要求id类型位String。适用于代理主键。
assigned由Java程序负责生成标识符,如果不指定id元素的gencrator属性,则默认使用该主键生成策略。适用于自然主键
hilo高低位方式(high low),需要一张额外的表保存hi的值,且至少由一条记录。

实体类操作

crud操作
操作代码
createsave(Object)
Retrieveget(Class, int id)
update1、根据id查询出来;2、重新设置查询出来的对象;3、调用:update(Object)
delete1、根据id查询;2、delete(Object);或者:1、创建实体对象,设置id;2、delete(Object)
实体类对象的三种状态
状态描述
瞬时态对象里面没有id值,且和session也没有关系(不是查询出来的)
持久态对象里面设置了id,且和session建立了关系(是session查询出来的),也就是在数据库中存储的数据
托管态对象设置了id,但和session没有关系
saveOrUpdate方法
  • 如果实体类是瞬时态,该方法执行新增;
  • 如果实体类是托管态,该方法执行修改;
  • 如果实体类是持久态,该方法执行修改;

hibernate缓存

什么是缓存?

  • 缓存就是将访问过的数据库数据等保存在系统的内存中,不需要使用流的方式就可以直接获取到,提高了读取效率。也减少了对数据库的访问。

hibernate的一种优化方式。

一级缓存
  • 默认是打开的

  • 使用范围:

session的范围,从session创建到关闭。

  • 存储的数据必须是持久态数据
验证一级缓存
  • 根据id查询,返回对象

第一次查询会访问数据库,控制台会打印sql语句

  • 再次根据id查询,返回对象

第二次查询会查询缓存,控制台不会打印sql语句

  • 代码
public class DemoTest {

	public static void main(String[] args) {
		// 加载核心配置文件
		Configuration cfg = new Configuration();
		cfg.configure();
		// 创建SessionFactory对象
		SessionFactory factory = cfg.buildSessionFactory();
		// 创建Session对象
		Session session = factory.openSession();
		// 开启事务
		Transaction tx = session.beginTransaction();

		// 查询操作
		System.out.println("第一次查询");
		User user = session.get(User.class, 1);
		System.out.println(user.getUsername());
		
		System.out.println("第二次查询");
		User user1 = session.get(User.class, 1);
		System.out.println(user1.getUsername());
	
		// 提交事务
		tx.commit();
		// 关闭资源
		session.close();
		factory.close();
	}
}
  • 结果
    在这里插入图片描述
一级缓存特性
  • 持久态的数据会自动更新,就算不调用update方法或者save方法;

过程:一级缓存会有一个快照区(副本),当我们修改持久态对象的值时,会同时更新一级缓存中的内容,但是不修改快照区内容。最后,提交事务时,会比较一级缓存和快照区的内容,如果不同,会把一级缓存中的数据更新到数据库中,相同则不会更新到数据库。

  • 代码
User user = session.get(User.class, 1);
user.setUsername("李四");
// 提交事务
tx.commit();
  • 结果
    在这里插入图片描述
二级缓存

现在使用redis替代了

  • 默认不打开,需要配置
  • 使用范围:sessionFactory范围

hibernate的事务操作

事务特性:

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

事务不考虑隔离性产生问题:

  • 脏读 - Read uncommitted isolation
  • 不可重复读 - Read committed isolation
  • 幻读 - Repeatable read isolation

hibernate配置MySQL隔离级别

  • 在核心配置文件中:

1 -> Read uncommitted isolation
2 -> Read committed isolation
4 -> Repeatable read isolation(mysql默认)
8 -> Serializable isolation

<property name="hibernate.connection.isolation">4</property>
事务代码规则写法
try {
			// 开启事务
			// 提交事务
} catch () {
			// 回滚事务
} finally {
			// 关闭事务
}

hibernate绑定session

底层是threadLocal实现的

实现本地线程绑定session
  • 获取本地线程绑定session
步骤代码
在hibernate核心配置文件中配置<property name="hibernate.current_session_context_class">thread</property>
调用sessionFactory的方法factory.getCurrentSession();
  • 这时就不需要进行手动关闭session

hibernate查询API

Query对象

(1)不需要写sql语句,但是要写hql(hibernate query language)语句
(2)hql和sql区别:
sql操作数据库表和字段
hql操作实体类和属性

查询所有
  • hql语句:from 实体类名称
  • 步骤
步骤
创建Query对象
调用方法获取结果
  • 代码
// 创建Query对象
Query query = session.createQuery("from User");
// 查询所有
List<User> list = query.list();
			
for(User u : list) {
	System.out.println(u.getUsername());
}
Criteria对象

不需要写任何语句,直接调用方法即可

  • 步骤
步骤
创建Criteria对象
调用方法获取结果
  • 代码
// 创建Criteria
Criteria c = session.createCriteria(User.class);
// 查询全部
List<User> list = c.list();
SQLQuery对象

调用底层sql进行查询,语句是原来的sql查询语句select * from 表名

  • 步骤
步骤
创建SQLQuery对象
设置返回结果的形式
调用方法获取结果
  • 代码
// 创建SQLQuery对象
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
// 调用方法实现,返回结果为一个个数组结果
List<Object[]> list = sqlQuery.list();

for(Object[] o : list) {
	System.out.println(Arrays.toString(o));
}

或者:

// 创建SQLQuery对象
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");

// 设置返回结果中每部分的形式
sqlQuery.addEntity(User.class);

// 调用方法获取结果
List<User> list = sqlQuery.list();

hibernate多表操作

多表关系
一对多(联系人和客户)

客户是一,联系人是多
一个客户对应多个联系人,一个联系人属于一个客户

  • 一对多建表:外键
    一的一方为主表,多的一方为副表。
    在这里插入图片描述
多对多(用户和角色)

一个用户对应多个角色,一个角色对应多个用户

  • 创建第三张表维护多对多关系

在这里插入图片描述

一对多操作
步骤备注
1、导jar包
2、新建客户、联系人实体类新建实体类时,如果要重写toString方法,不要把第3步创建的属性也包括进去哦,否则会栈溢出!!!
3、让两个实体类之间互相表示在实体类中添加属性,表示:一个客户有多个联系人,一个联系人属于一个客户。 hibernate要求使用set表示多的数据。
4、配置映射关系(详见代码)Ⅰ、每个实体类都要有对应的映射文件;Ⅱ、完成基本配置;Ⅲ、配置对应的一对多关系
5、创建核心配置文件(详见代码)Ⅰ、配置数据库连接;Ⅱ、配置hibernate相关;Ⅲ、引入映射配置文件
6、测试
一对多映射配置
  • 客户实体类

使用Set集合表示多个联系人

public class Customer {
	// 客户id
	private Integer cid;
	// 客户名称
	private String cName;
	// 客户性别
	private String cSex;
	// 客户级别
	private String cLevel;
	// 联系电话
	private String cPhone;
	
	// 多个联系人(重点)
	private Set<LiaisonMan> lmans = new HashSet<>();
	
	// 这里需要生成所有属性的get、set方法
}
  • 客户映射文件

文件名为:Customer.hbm.xml,配置时建议根据实体类属性顺序进行配置。重点注意“多个联系人”的配置方式。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="demo.test.pojo.Customer" table="t_customer">
		<id name="cid" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="cName" column="cName"></property>
		<property name="cSex" column="cSex"></property>
		<property name="cLevel" column="cLevel"></property>
		<property name="cPhone" column="cPhone"></property>
		
		<!-- 多个联系人
			name: 联系人集合属性名
			key->column:外键名称
			class: 联系人所在的类 
		 -->
		<set name="lmans">
			<!-- hibernate中双向维护外键-->
			<key column="clid"></key>
			<one-to-many class="demo.test.pojo.LiaisonMan"/>
		</set>
	</class>
</hibernate-mapping>
  • 联系人实体类

使用客户实体类的实例表示一个客户;

public class LiaisonMan {
	// 联系人id
	private Integer lid;
	// 联系人姓名
	private String lname;
	// 联系人性别
	private String lsex;
	// 联系人电话
	private String lphone;
	
	// 属于一个客户(重点)
	private Customer customer;
	
	// 这里需要生成所有属性的get、set方法
}
  • 联系人映射文件

文件名为:LiaisonMan.hbm.xml,配置时建议根据实体类属性顺序进行配置。重点注意“所属客户”的配置方式

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="demo.test.pojo.LiaisonMan" table="t_liaisonMan">
		<id name="lid" column="lid">
			<generator class="native"></generator>
		</id>
		<property name="lname" column="lname"></property>
		<property name="lsex" column="lsex"></property>
		<property name="lphone" column="lphone"></property>
		
		<!-- 所属客户
			name: 客户属性名、
			class: 客户类
			column: 外键名称
		 -->
		<many-to-one name="customer" class="demo.test.pojo.Customer" column="clid"></many-to-one>
	</class>
</hibernate-mapping>
  • 核心配置文件

文件名为:hibernate.cfg.xml

<?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/hibernate?serverTimezone=UTC</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">leidada</property>
		
		<!-- 第二部分:配置hibernate信息 -->
		<!-- 打印sql语句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 对sql语句进行格式化 -->
		<property name="hibernate.format_sql">true</property>
		<!-- 开启表的自动更新
			update: 无表创建,有表更新
		 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 如果使用分页,需要配置数据库方言,使其能够识别各自的语言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
		
		<!-- 第三部分:配置映射文件 -->
		<mapping resource="demo/test/pojo/Customer.hbm.xml"/>
		<mapping resource="demo/test/pojo/LiaisonMan.hbm.xml"/>
	</session-factory>
</hibernate-configuration>
  • 测试代码

static中的代码在类最初被加载时就会运行,而SessionFactory的创建代表着数据库表的创建。

public class HibernateTest {
	private static Configuration cfg = null;
	private static SessionFactory factory = null;
	
	static {
		// 读取配置文件
		cfg = new Configuration();
		cfg.configure();
		// 获取SessionFactory
		factory = cfg.buildSessionFactory();
	}
	public static void main(String[] args) {
		
	}
}
  • 测试效果

成功创建两张数据库表,说明配置是正确的!!!

在这里插入图片描述

一对多级联保存
  • 添加一个客户,为这个客户添加多个联系人
  • 复杂实现方式
public class OneToManySaveTest {
	private static Configuration cfg = null;
	private static SessionFactory factory = null;
	
	static {
		// 读取配置文件
		cfg = new Configuration();
		cfg.configure();
		// 获取SessionFactory
		factory = cfg.buildSessionFactory();
	}
	public static void main(String[] args) {
		
		Session session = null;
		Transaction tx = null;
		try {
			session = factory.openSession();
			// 开启事务
			tx = session.beginTransaction();
			
			// 级联保存
			// 1、添加一个客户,为这个客户添加两个联系人
			Customer c = new Customer();
			c.setcName("小李");
			c.setcSex("男");
			c.setcLevel("vip");
			c.setcPhone("123456");
			
			
			LiaisonMan l_1 = new LiaisonMan();
			l_1.setLname("A");
			l_1.setLsex("女");
			l_1.setLphone("123789");
			LiaisonMan l_2 = new LiaisonMan();
			l_2.setLname("B");
			l_2.setLsex("男");
			l_2.setLphone("789789");
			
			
			// 2、建立 客户-联系人 的关系
			c.getLmans().add(l_1);
			c.getLmans().add(l_2);
			
			l_1.setCustomer(c);
			l_2.setCustomer(c);
			
			// 3、保存
			session.save(c);
			session.save(l_1);
			session.save(l_2);
			
			// 提交事务
			tx.commit();
		}catch(Exception e) {
			e.printStackTrace();
			// 事务回滚
			tx.rollback();
		}finally {
			// 关闭资源
			session.close();
			factory.close();
		}
	}
}
  • 效果
    在这里插入图片描述

  • 简便方式(-----------------)

步骤
需求:根据客户添加联系人
1、在客户的映射文件中的<set>标签中进行配置,添加save-update
2、将联系人放入到客户中即可,不需要将客户再放入到联系人中,且最后只需要保存客户即可
和上面代码的不同在于:代码中的2、和3、

配置内容:
在这里插入图片描述代码:

在这里插入图片描述

  • 效果
    在这里插入图片描述
一对多级联删除
  • 删除一个客户,同时删除这个客户的所有联系人
步骤
1、在客户的映射文件中的<set>标签中进行配置,添加delete
2、先查出要删除的用户,再调用方法删除

配置如下:
在这里插入图片描述
代码:
在这里插入图片描述

  • 效果

在这里插入图片描述

一对多级联修改
  • 修改前
    在这里插入图片描述
步骤
需求:将联系人A的所属客户修改为小苏,也就是clid修改为2
1、查询出需要被修改的联系人A
2、查询出被修改为的客户小苏,(把小李修改为小苏,所以查询出小苏来)
3、将A和小苏关联
  • 代码
// 级联修改 - 将A的客户由小李修改为小苏

// 1、根据id查询 客户-小苏
Customer customer = session.get(Customer.class, 2);
// 2、根据id查询联系人A
LiaisonMan A = session.get(LiaisonMan.class, 5);
// 3、设置持久态对象值
// 把联系人放入客户中
customer.getLmans().add(A);
// 把客户放到联系人中
A.setCustomer(customer);
  • 修改后效果
    在这里插入图片描述- inverse属性

存在问题:

由于hibernate中的外键是由双方共同维护的,所以,在下面过程中可以看到,对外键进行了两次修改,但我们在这个过程中只需要修改一次联系人和外键即可,第二次对外键的修改可以不进行。考虑到效率,要进行优化。
在这里插入图片描述

解决方式:

让其中的一方不去维护外键。一对多中,让“一”的这方放弃维护外键。所以,我们让customer放弃对外键的维护!!!

步骤
在customer的映射配置文件中的<set>标签中,添加inverse="true"

在这里插入图片描述效果:

只维护外键一次
在这里插入图片描述

多对多操作
步骤备注
1、导jar包
2、新建用户、角色 实体类
3、让两个实体类之间互相表示Ⅰ、用户中使用set集合表示所有角色;Ⅱ、角色中使用set集合表示所有用户;
4、配置映射关系(详见代码)Ⅰ、基本配置;Ⅱ、配置多对多关系,使用<set>标签------(配置的内容是:①当前实体类在第三张表中的外键名称<key>;②要关联多个关系的实体类的类名+在第三张表中的外键名称<many-to-many);注意要对应上哈!!!
5、创建核心配置文件(详见代码)
6、测试
多对多映射配置
  • 用户实体类
public class User {
	// 用户id
	private Integer uid;
	// 用户名
	private String username;
	// 密    码
	private String password;
	
	// 多个角色
	private Set<Role> roles = new HashSet<>();

	// 下面请给出所有属性的get和set方法
}
  • 用户映射文件

文件名为:User.hbm.xml

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
		<class name="demo.test.pojo.User" table="t_user">
		<id name="uid" column="uid">
			<generator class="native"></generator>
		</id>
		
		<property name="username" column="username"></property>
		<property name="password" column="password"></property>
		
		<!-- 多个角色
			name: 角色集合名称
			table: 第三张表的名称
		 -->
		<set name="roles" table="t_user_role">
			<!-- 配置当前user在第三张表中的外键名称 -->
			<key column="user_id"></key>
			<!-- 配置多个角色所在的类,以及角色在第三张表中的外键名称 -->
			<many-to-many class="demo.test.pojo.Role" column="role_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>
  • 角色实体类
public class Role {
	// 角色id
	private Integer rid;
	// 角色名
	private String rolename;
	
	// 多个用户
	private Set<User> users = new HashSet<>();
	
	// 下面请给出所有属性的get和set方法
}
  • 角色映射文件

文件名为:Role.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="demo.test.pojo.Role" table="t_role">
		<id name="rid" column="rid">
			<generator class="native"></generator>
		</id>
		<property name="rolename" column="rolename"></property>
		
		<!-- 多个用户
			name: 用户集合名称
			table: 第三张表的名称 -->
		<set name="users" table="t_user_role">
			<!-- 配置当前角色在第三张表中的外键名称 -->
			<key column="role_id"></key>
			<!-- 配置多个用户所在的类,以及用户在第三张表中的外键名称 -->
			<many-to-many class="demo.test.pojo.User" column="user_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>
  • 核心配置文件

文件名为:hibernate.cfg.xml

<?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/hibernate?serverTimezone=UTC</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">leidada</property>
		
		<!-- 第二部分:配置hibernate信息 -->
		<!-- 打印sql语句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 对sql语句进行格式化 -->
		<property name="hibernate.format_sql">true</property>
		<!-- 开启表的自动更新
			update: 无表创建,有表更新
		 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 如果使用分页,需要配置数据库方言,使其能够识别各自的语言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
		
		<!-- 第三部分:配置映射文件 -->
		<mapping resource="demo/test/pojo/Role.hbm.xml"/>
		<mapping resource="demo/test/pojo/User.hbm.xml"/>
	</session-factory>
</hibernate-configuration>
  • 测试代码

SessionFactory 创建意味着数据库表的创建。

public class HibernateTest {
	private static Configuration cfg = null;
	private static SessionFactory factory = null;
	
	static {
		// 读取配置文件
		cfg = new Configuration();
		cfg.configure();
		// 获取SessionFactory
		factory = cfg.buildSessionFactory();
	}
	public static void main(String[] args) {
	}
}
  • 测试效果

共创建了三张表,说明配置成功!!!

在这里插入图片描述

多对多级联保存
  • 添加用户,同时添加角色
  • 直接使用简便方式,在用户配置文件中的<set>标签中添加save-update属性
    在这里插入图片描述
  • 编写测试代码

1、两个用户对象
2、三个角色对象

public class HibernateTest {
	private static Configuration cfg = null;
	private static SessionFactory factory = null;
	
	static {
		// 读取配置文件
		cfg = new Configuration();
		cfg.configure();
		// 获取SessionFactory
		factory = cfg.buildSessionFactory();
	}
	public static void main(String[] args) {
		Session session = null;
		Transaction tx = null;
		try {
			// 创建session
			session = factory.openSession();
			// 开启事务
			tx = session.beginTransaction();
			// 级联保存
			// 1、创建用户
			User u1 = new User();
			u1.setUsername("小A");
			u1.setPassword("123456");
			
			User u2 = new User();
			u2.setUsername("小B");
			u2.setPassword("123456");
			// 2、创建角色
			Role r1 = new Role();
			r1.setRolename("老板");
			
			Role r2 = new Role();
			r2.setRolename("经理");
			
			Role r3 = new Role();
			r3.setRolename("员工");
			// 3、建立联系:
			// 	 u1 -> r1/r2
			// 	 r2 -> r2/r3
			u1.getRoles().add(r1);
			u1.getRoles().add(r2);
			
			u2.getRoles().add(r2);
			u2.getRoles().add(r3);
			
			// 4、保存用户
			session.save(u1);
			session.save(u2);
			
			// 提交事务
			tx.commit();
		}catch(Exception e) {
			e.printStackTrace();
			// 事务回滚
			tx.rollback();
		}finally {
			// 关闭资源
			session.close();
			factory.close();
		}
	}
}
  • 测似效果
    在这里插入图片描述
多对多级联删除
步骤
1、在客户的映射文件中的<set>标签中进行配置,添加delete
2、先查出要删除的用户,再调用方法删除

在这里插入图片描述- 代码
在这里插入图片描述

  • 测试效果

问题:可以看到,再删除第一个用户时,将两个用户共同的角色也给删除掉了。所以,多对多情况下一般不进行级联删除。

在这里插入图片描述

维护第三张表关系
  • 用户和角色的多对多关系,是通过维护第三张表来进行的;

关系图
小A:老板、经理
小B:经理、员工
在这里插入图片描述

  • 对持久态数据进行修改后,可以不调用save或update方法也会进行数据库修改,因为hibernate一级缓存的特性!!!
  • 操作:让某个用户有某个角色
步骤
1、根据id查询用户和角色
2、把角色放到用户的角色set集合中即可
目的:小B也要当老板!!!

代码
在这里插入图片描述

效果:
在这里插入图片描述

  • 操作:让某个用户没有某个角色
步骤
1、根据id查询用户和角色
2、把该角色从用户的角色set集合中去掉即可
目的:小B不要当经理!!!

代码:
在这里插入图片描述
效果:
在这里插入图片描述

Hibernate查询方式

衔接上面***多对多查询***的案例进行总结!

对象导航查询
  • 根据id查询用户,再查询出该用户的所有角色信息

查询小A以及小A的所有角色信息
在这里插入图片描述

OID查询
  • 根据id查询某一条记录,返回对象

根据id查询小A信息
在这里插入图片描述

hql查询

hql操作的是对象及其属性;sql操作的是数据库表以及其字段;
使用:Query对象及其方法。

  • 查询所有

语句:from 实体类名

在这里插入图片描述- 条件查询

语句:from 实体类名 as 别名 where 别名.实体类属性名=? and 别名.实体类属性名=?,注意:?代表占位符,下标从0开始!

// 条件查询
Query query = session.createQuery("from User as u where u.uid=? and u.username=?");
// 设置占位符的值
query.setParameter(0, 1);
query.setParameter(1, "小A");
// 调用方法
List<User> list = query.list();
  • 模糊查询

语句:from 实体类名 where 实体类属性名 like ?

// 模糊查询
Query query = session.createQuery("from User where username=?");
// 设置占位符的值
query.setParameter(0, "%A%");
// 调用方法
List<User> list = query.list();
  • 排序查询

语句:from 实体类名 order by 实体类属性名称 asc/desc

// 排序查询
Query query = session.createQuery("from User order by uid asc");
// 调用方法
List<User> list = query.list();
  • 分页查询

不能写limit,而是使用Query对象封装的两个方法实现:

// 分页查询
Query query = session.createQuery("from User");
// 设置分页数据
// 1、设置开始位置
query.setFirstResult(0);
// 2、设置每页记录数
query.setMaxResults(2);
// 调用方法
List<User> list = query.list();
  • 投影查询

什么是投影查询?不查询全部,而是查询数据库中的部分字段。
语句:select 实体类属性名,... from 实体类名,注意,select后不能写*

// 投影查询
Query query = session.createQuery("select username from User");
// 调用方法:泛型不再是实体类哦!
List<Object> list = query.list();
  • 聚合函数的使用

查询表记录数量:
语句:select count(*) from User

// 聚集函数的使用:sum、count、avg、max、min等
Query query = session.createQuery("select count(*) from User");
// 调用方法:返回值不再是list
Object result = query.uniqueResult();
System.out.println(result);
QBC查询

使用:Criteria对象及其方法,不用写语句

  • 查询全部
// 查询所有
Criteria c = session.createCriteria(User.class);
// 调用方法
List<User> list = c.list();
  • 条件查询

Restrictions类的使用

// 条件查询
Criteria c = session.createCriteria(User.class);
// 设置条件
c.add(Restrictions.eq("username", "小A"));
//调用方法
List<User> list = c.list();
  • 排序查询

Order类的使用

// 排序查询
Criteria c = session.createCriteria(User.class);
// 设置排序规则
c.addOrder(Order.asc("uid"));
//调用方法
List<User> list = c.list();
  • 分页查询

开始位置计算公式:(当前页-1)* 每页记录数

// 分页查询
Criteria c = session.createCriteria(User.class);
// 设置分页信息
c.setFirstResult(0);//开始位置
c.setMaxResults(2);//每页记录数
//调用方法
List<User> list = c.list();
  • 统计查询

统计表中记录数,Projections的使用

//统计查询
Criteria c = session.createCriteria(User.class);
//设置统计条件
c.setProjection(Projections.rowCount());
//调用方法得到结果
Object obj = c.uniqueResult();
long l = (long) obj; //不能直接转int
int i = (int) l;
System.out.println(i);
  • 离线查询

离线查询:不使用session对象也能进行查询;
DetachedCriteria类的使用

// 离线查询
DetachedCriteria dc = DetachedCriteria.forClass(User.class);
// 最终执行的时候才需要得到session
Criteria c = dc.getExecutableCriteria(session);
//调用方法
List<User> list = c.list();
System.out.println(list.get(0).getUsername());
本地sql查询
  • SQLQuery对象,使用普通sql实现查询

HQL多表查询

在一对多条件下,假装:User对Role是一对多关系,一个User对应多个Role,Role中有外键uid,关联User的uid。

内连接

普通sql:select * from User u, Role r where u.uid = r.uid
---------或者:select * from User u inner join Role r on u.uid = r.uid

使用inner join
HQL:from 实体类名 inner join (实体类中多的哪一方set集合的属性名)
返回结果的list中,每部分是数组形式!!!

// 内连接
Query query = session.createQuery("from User u inner join u.roles");
//调用方法
List<User> list = query.list();
左外连接

左外连接查询的是:左表全部,右表符合条件的
普通sql:select * from User u left join Role r on u.uid = r.uid

使用left outer join
HQL:from 实体类名 left outer join(实体类中多的哪一方set集合的属性名)
返回结果的list中的每部分是数组形式!!!

// 左外连接
Query query = session.createQuery("from User u left outer join u.roles");
//调用方法
List<User> list = query.list();
右外连接

右外连接查询的是:右表全部,左表符合条件的
普通sql:select * from User u right join Role r on u.uid = r.uid

使用right outer join
HQL:from 实体类名 right outer join (实体类中多的哪一方set集合的属性名)
返回结果的list中的每部分是数组形式!!!

// 左外连接
Query query = session.createQuery("from User u right outer join u.roles");
//调用方法
List<User> list = query.list();
迫切内连接(独有)

迫切内连接和内连接的底层实现是一样的;
区别:
1、普通内连接返回list的每部分是数组;
2、迫切内连接返回list的每部分是对象类型;

使用inner join fetch
HQL:from 实体类名 inner join fetch(实体类中多的哪一方set集合的属性名)
返回结果的list中,每部分是对象形式!!!

// 内连接
Query query = session.createQuery("from User u inner join fetch u.roles");
//调用方法
List<User> list = query.list();
迫切左外连接(独有)

和左外连接区别:返回结果是数组形式还是对象形式

使用left outer join fetch
HQL:from 实体类名 left outer join fetch(实体类中多的哪一方set集合的属性名)
返回结果的list中每部分是对象形式!!!

// 左外连接
Query query = session.createQuery("from User u left outer join fetch u.roles");
//调用方法
List<User> list = query.list();

Hibernate检索策略

检索策略的概念
立即检索(查询)

调用get方法,马上发送语句查询;

// 根据id查询 - 立即查询
User user = session.get(User.class, 1);
System.out.println(user.getUsername());
延迟检索(查询)
  • 类级别延迟

调用load方法,不会马上发送语句查询,只有等到想获取对象中,除id属性的其他属性时,才会发送语句查询。也就是说,只有用到对象中除id的其他属性时才会查询。

//类级别延迟
//根据id查询 - 延迟查询(可用debug模式查看)
// 返回的结果只有id值
User user = session.load(User.class, 1);
System.out.println(user.getUid());
System.out.println("开始发送语句,请查看控制台输出");
// 只有想得到除id的值外的其他数据时,这时才发送查询语句
System.out.println(user.getUsername());
  • 关联级别延迟

只有用到关联类的数据时,才查询其关联类的数据;
比如:查询用户数据后,用到其角色数据时,才查询其关联的所有角色的信息。
get方式默认使用了关联级别延迟

//关联级别延迟
//根据id查询 - 延迟查询(可用debug模式查看)
//返回的结果只有id值
User user = session.get(User.class, 1);
System.out.println(user.getUid());
System.out.println("开始发送语句,请查看控制台输出");

//只有想得到除id的值外的其他数据时,这时才发送查询语句
System.out.println(user.getRoles());
检索策略<set>

使用配置选择是否进行关联级别延迟
在这里插入图片描述

fetchlazy策略
joinfalse采用迫切左外连接检索
jointrue采用迫切左外连接检索
joinextra采用迫切左外连接检索
selectfalse采用立即检索
selecttrue采用延迟检索
selectextra采用极其延迟检索,要什么查什么,绝不多查,比如:需要得到list的size时,hibernate会查询count()值
subselectfalse/true/extra嵌套子查询
批量抓取
  • 使用场景:
    批量获取关联查询的数据时,可以进行优化。
    比如:遍历用户,获取所有用户的所有角色信息时,因为默认使用延迟加载,在获取角色信息时会频繁发送sql查询语句。使用批量抓取,可以减少查询语句发送的次数,提高效率。

<set>中配置batch-size属性,其值越大效率越高

步骤
配置<set>

在这里插入图片描述

// 查询所有
Criteria c = session.createCriteria(User.class);
// 调用方法
List<User> list = c.list();
// 遍历所有用户,获取角色信息,用批量抓取优化
for(User u : list) {
	// 获取每个用户里面所有的角色信息
	Set<Role> roles = u.getRoles();
	for(Role r : roles) {
		System.out.println(r.getRolename());
	}
}

做笔记太累了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值