上连接到Hibernate--002 session简单操作数据库、Hibernate中存储数据的三种状态、脏数据与刷新缓存。
https://blog.csdn.net/hekaikai666/article/details/81876861
回顾与总结:
简述上一章的主要内容:
(一)session、sessionFactory、Configuration三大接口的简介
(二)Hibernate简单操作数据库的方式
(三)Hibernate照片那个存数据数据的三种状态
游离态、持久态、临时态
(四)Hibernate中数据的脏读、与刷新缓存机制
接下来的全都是重点哟!
一、Hibernate中数据查询的三种方式
(一)HQL语句(重点)
1.HQL(Hibernate Query Language):Hibernate中定义的一种结构化查询语句,是一种面向Java实体类的面向对象查询语言,与SQL有着相似的语法结构,但是与SQL么有一丝丝的关系。
2.SQL和HQL的区别:
a.SQL是一种面向数据库的查询语言,是按照表名,列名来查询的。(SELECT 列名1,列名2,... FROM ...)而HQL是一种面向实体类的查询语言,是按照类名,属性名来查询的(SELECT 属性名1,属性名2,... FROM 表名)。
b.HQL不支持“SELECT * ” 的写法,因为默认的为"SELECT * "。
c.HQL不支持分组函数以外的其他函数(分组函数:COUNT()/AVG()/MIN()/MAX()/SUM())。
d.HQL不支持JOIN...ON...的连接查询
3.HQL的使用。(重中之重)
a.通过HQL语句来查询(执行HQL的方法有:list() iterate() uniqueResult())
b.使用步骤:
b_1:获取session对象
b_2:定义HQL语句
b_3:创建query对象疯长HQL语句
b_4:调用query方法执行HQL语句(lis() iterate() nuiqueResult())
b_5:关闭session
HQL查询:使用三种方法(list() iteate() uniqueRequest())
// 定义查询所有的HQL语句(Dept是类名,deptNo是属性)
String hql = "from Dept where deptNo=1";
// 创建一个查询对象Query对象封装一个HQL语句
Query query = session.createQuery(hql);
// 当确定返回值只有一个值,调用uniqueResult
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// 根据id查询名字
// 执行HQL语句(调用list方法执行HGQL语句)
List<Dept> depts = query.list();
System.out.println(depts);
// 关闭session
hql = "select deptName from Dept where deptNo=1";
query = session.createQuery(hql);
String str = (String) query.uniqueResult();
System.out.println(str);
Iterator<Dept> iterate = query.iterate();
while (iterate.hasNext()) {
Dept dept1 = iterate.next();
System.out.println(dept1.getDeptNo());
System.out.println(dept1.getDeptName());
System.out.println(dept1.getDeptLocation());
System.out.println("-----------------------------------");
}
// 关闭session对象
session.close();
执行HQL的三种方法:
list():当调用此方法时会立即向数据库中发送一条查询语句(SQL),查询所有的对象信息并保存到list<T>集合中;
iterate():当调用此方法时会启动延时加载,向数据库中发送一条查询SQL语句,先查询所有对象的id属性值,返回的是代理对象(只有id),然后将这些id对象保存到iterate迭代器对象中,当我们迭代某个对象的时候,需要查询此个对象的非id属性,会拿出此个对象的id,根据对象的id查询此个对象的完整信息。
uniqueResult():非延时加载,当确定返回对象只有一份时使用。
4.给带占位符的HQL语句赋值(三种占位符---?占位符 | :属性名 占位符 |命名参数给定占位符)
a.如果占位符是"?":通过占位符下标,下标从"0"开始赋值
query.setString(下标,属性值); query.setParameter(下标,属性值);
b.如果站位符是":属性名" :通过属性赋值
query.setString(属性名,属性值); query.setParameter(属性名,属性值);
c.命名参数给占位符赋值:
map:key:属性名;value:属性值
query.setProperties(map);
案例:
// ?占位符
// 定义一个带占位符的hql语句
String hql = "from Dept where deptName=? and deptLocation=?";
//创建一个查询对象Query对象封装一个HQL语句
Query query = session.createQuery(hql);
// 给占位符赋值(下标从0开始)
query.setString(0, "凯凯");
query.setString(1, "西安");
// 给占位符赋值(下标从0开始)
query.setParameter(0, "凯凯");
query.setParameter(1, "西安");
// 执行hql语句
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// :属性名 占位符
// 定义一个带占位符的hql语句
String hql1 = "from Dept where deptName=:deptName and deptLocation=:deptLocation";
//创建一个查询对象Qu【】ery对象封装一个HQL语句
Query query = session.createQuery(hql);
// 给占位符赋值(下标从0开始)
query.setString(0, "凯凯");
query.setString(1, "西安");
// 给占位符赋值(下标从0开始)
query.setParameter(0, "凯凯");
query.setParameter(1, "西安");
// 执行hql语句
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// 命名参数 占位符(要求占位符必须是属性)
// 定义一个带占位符的hql语句
String hql1 = "from Dept where deptName=:deptName and deptLocation=:deptLocation";
//创建一个查询对象Query对象封装一个HQL语句
Query query = session.createQuery(hql);
Map<String, String> map = new HashMap<String,String>();
map.put("deptName", "凯凯");
map.put("loactiom", "西安");
query.setProperties(map);
Dept dept1 = (Dept) query.uniqueResult();
System.out.println(dept1);
5.分页查询
在之前学过的SQL语句查询中分页会用一个关键字来查(limit)例如:SELECT * FROM emp limit ?,?;需要给定的两个参数为从那一条开始查P1,需要查询多少条P2,所以分页查询HQL中也必须知道这俩参数:page(当前页码) pageCount(页面容量)。
page pageCount P1 P2
1 3 0 3
2 3 1 3
3 3 2 3
4 3 3 3
由此可得:P1 = (page-1)*pageCount; P2 = pageCount;
由此可得String hql = "SELECT COUNT(*) FROM emp" --> rows;
总页码数为:long totalPages = rows % pageCount == 0 ? rows / pageCount : rows / pageCount + 1;
// 定义hql
String hql = "from Emp";
// 创建query对象封装hql
Query query = session.createQuery(hql);
// 获取页码和页容
int page = 2;
int pageCount = 2;
// 通过对象设置开始的条数
query.setFirstResult((page - 1) * pageCount);
// 设置查询结果的最大条数
query.setMaxResults(pageCount);
// 执行HQL语句,获取当前页面内容
List<Emp> emps = query.list();
for (Emp emp : emps) {
System.out.println(emp);
}
// 查看总共有多少页
// 先查询总共有多少条记录
hql = "select count(*) from Emp";
// 封装hql语句
query = session.createQuery(hql);
//获取返回的总行数
long rows = (long) query.uniqueResult();
// 获取拿到的页码数(分页)
long totalPages = rows % pageCount == 0 ? rows / pageCount : rows / pageCount + 1;
System.out.println("总条数:" + rows);
System.out.println("总页数:" + totalPages);
// 关闭session
session.close();
(二)动态参数查询
主要借助两个API(这两API内容过多,掌握其几个方法即可,关于动态查询其他方法详析,请参照之后的贴子)
Criteria:主要用于封装多个动态查询条件、并执行查询操作
Restrictions:用于封装一种某一个动态查询的参数,提供了很多静态方法,用于代替查询调条件
案例:
// 创建一个Criteria对象,可以封装多个动态查询参数
Criteria criteria = session.createCriteria(Emp.class);
// 实例化一个时间格式类
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 如:查询职位为ClERK的员工信息|||Restrictions封装某一个动态查询参数..
criteria.add(Restrictions.eq("ejob", "CLERK"));
// 在添加一个动态查询参数,查询薪水大于5000的员工信息
criteria.add(Restrictions.gt("salary", Double.valueOf(5000)))
.add(Restrictions.between("hiredate", df.parse("2015-01-01"), df.parse("2017-12-31")));
// 再添加一个动态查询参数
// 查询2014年入职的并且薪水小于5000的员工信息
criteria = session.createCriteria(Emp.class);
criteria.add(Restrictions.and(Restrictions.gt("hiredate", df.parse("2014-01-01")),
Restrictions.lt("salary", Double.valueOf(5000))));
// 执行查询
List<Emp> ls = criteria.list();
// 遍历获取
for (Emp emp : ls) {
System.out.println(emp);
}
(三)原生SQL查询
定义SQL语句;
使用SQLQuery封装SQL语句【SQLQuery query = session.createSQLQuery(sql).addEntity(Emp.class);】;
通过query执行SQL语句;
// 定义SQL语句
String sql = "select * from Emp";
// 创建SQL查询,用于封装SQL语句
SQLQuery query = session.createSQLQuery(sql).addEntity(Emp.class);
// 执行SQL语句
List<Emp> ls = query.list();
// 遍历获取值
for (Emp emp : ls) {
System.out.println(emp);
}
// 关闭session对象
session.close();
注:因为原生SQL查询和动态参数查询都是比较简单的查询方式,只需要了解几个常用API的方法,所以在这里不做详细的解释,如果需要使用或者需要详细的解析,可以联系博主小凯,可以给你详细的解释。
二、Hibernate的关联查询
在关系型数据库中数据的存储方式需要按照三大范式和第四范式的标准执行,所以对于多种表结构单表查询肯定不实用,那么对于HQL这种简单的单表操作方式就用不上了,Hibernate中封装了许多的映射方法,用于查询多表之间的操作(这里只演示两表操作,与多表操作基本一致)所以就有了关系数据库中的一对多,多对一,多对多,一对一的层级关系来管理关系数据表。
(一)一对多(one-to-many)与多对一(many-to-one)
【例】一个学生student有多本数书books,多本书books对应一个学生student
操作流程:
a.建表
#学生表
CREATE TABLE student(
s_id int primary key,
s_name varchar(20));
#图书表
CREATE TABLE book(
b_id int primary key,
b_name varchar(20),
b_author varchar(20),
b_studentid int);
#建立外键关系
alter table book add foreign key(b_studentid) reference student(s_id);
b.建实体类
一方实体类 -s_id -s_name -Set<Book> set #表示多方
多方实体类 -b_id -b_name -b_author Student student #表示一方
public class Student implements Serializable {
private int s_id;
private String s_name;
// 表示多方集合:一个学生有多本书
private Set<Book> books = new HashSet<Book>();
此处省略空参构造方法,多参构造方法,get方法,set方法,toString方法
}
public class Book implements Serializable {
private int b_id;
private String b_name;
private String b_author;
// 表示当前book属于哪个student
private Student student;
此处省略空参构造方法,多参构造方法,get方法,set方法,toString方法
}
c. 映射文件
一方映射:student.hbm.xml
<set name="多方集合属性名">
<key column="外键"/>
<one-to-many class="多方实体类全限定名"/>
</set>
多方映射:book.hbm.xml
<many-to-one name="一方实体类属性名" column="外键" class="一方实体类全限定名"/>
<class name="com.lanou.beans3.Student" table="student">
<id name="s_id" type="int" column="s_id">
<generator class="assigned" />
</id>
<property name="s_name" type="string" column="s_name" />
<!-- 一对多的关联映射 -->
<set name="books" lazy="false" cascade="all">
<key column="b_studentid"/>
<one-to-many class="com.lanou.beans3.Book"/>
</set>
</class>
<class name="com.lanou.beans3.Book" table="book">
<id name="b_id" type="int" column="b_id">
<generator class="assigned" />
</id>
<property name="b_name" type="string" column="b_name" />
<property name="b_author" type="string" column="b_author" />
<!-- 定义多对一的关联映射 -->
<many-to-one name="student" column="b_studentid" lazy="false"
cascade="delete" class="com.lanou.beans3.Student" />
</class>
属性解释:
一对多的关联映射
name:第一方实体类中用来描述多方的集合属性名;column:多方表中用于维持关系的外键名称;class:多方实体类全限定名lazy:true默认延时加载,只加载当前对象 false:取消延时加载,级联查询所关联的对象信息;cascade:级联操作;值:none:默认值,只操作当前对象;save-update:级联的添加或者修改其关联对象;delete:级联的删除其关联对象;all:包含save-update/delete。
定义多对一的关联映射
name:多方实体类中用于描述一方实体类属性名 ;column:多方表中用于维持关系的外键名称 ;class:一方实体类全限定名;lazy:取消延时加载(false)默认值,延迟加载,使用时获取的是代理对象(proxy) 延迟加载,使用时获取的是非代理对象(no-proxy)
d.级联操作
d.级联操作
d_1.一对多的级联查询(查询id为1的学生有哪些书)
d_2.一对多的级联修改(给id为2的学生添加两本书)
d_3.一对多的级联添加(添加一个学生,并级联添加两本图书)
d_4.多对一的级联查询(查询金瓶梅这本书属于哪个学生)
d_5.多对一的级联删除(删除金瓶梅这本书以及它所关联的学生)
底层原理实现:
1、先插多方的外键b_studentid
2、通过外键b_studentid查询到所关联的Student(s_id)
3、再从book中查询哪些书是b_studentid等于s_id
4、删除这些book
5、删除这些学生
以级联查询为例
// 查询参数为id的student对象
Student student = (Student) session.get(Student.class, 1);
System.out.println(student.getS_id());
System.out.println(student.getS_name());
// 获取其多方关联的集合信息
Set<Book> books = student.getBooks();
for (Book book : books) {
System.out.println(book.getB_id());
}
(二)多对多(many-to-many)
【例】一个员工employee会做多个项目project;一个项目project可能会由多个员工employee完成
a.建表
#employee员工表
create table employee(e_id int primary key,e_name varchar(20));
#project项目表
create table project(p_id int primary key,p_name varchar(20));
#中间表(关系表)
create table proemp(id int primary key,auto_increament,rempid int,#表示员工rproid int #表示项目);
b.实体类
Employee:--int e_id --String e_name --Set<Project> projects
Project:--int p_id --String p_name --Set<Employee> employees
c.映射文件
<set name="描述对方的集合属性" table="中间表" cascade="all">
<key column="当前表在中间表的描述id" />
<many-to-many class="对方实体类全限定名" column="对方表在中间表的描述id" />
</set>
(二)一对一(one-to-one)
一对一映射必须要有一个关联,就要考虑其关联的外键还是主键,单向还是双向,这种关联肯定要和自己本身的属性有,所以可以分为单向外键关联,双向外键关联,单向主键关联,双向主键关联
【例】一个用户user对应一行身份证card;一张身份证card对应一个用户user
(单向外键关联)(双向外键关联)(单向主键关联)(单向主键关联)
a.建表
#用户表
create table user(u_id int primary key,u_name varchar(20),u_address varchar(20));
#身份证表
create table card(c_id int primary key,c_No varchar());
b.建实体类
User --u_id --u_name --u_address --Card card
Card --c_id --c_No
c.映射文件
<one-to-one name="当前类中描述对方的实体类" class="对方实体类全限定名" cascade="all"/>
这里,Hibernate中所有存在性查询已经完全结束了,但是他的加载过程还远远没有结束,在下一贴中将会分享关联映射和缓存机制,这才是Hibernate的第四优势。