JavaEE的三层结构
- web层:struts2框架
- service层:spring框架
- dao层:hibernate框架
MVC思想 - m:模型
- v:视图
- c:控制器
1 Hibernate概述
1.1 框架
1.1.1 通俗理解
就是少写部分代码实现功能
1.2 Hibernate框架
- hibernate框架应用在JavaEE三层结构中的dao层
- 在dao层hibernate实现CRUD操作,hibernate底层就是JDBC,对JDBC进行封装,好处是不需要写复杂的
JDBC代码了,不需要写SQL语句实现 - hibernate开源轻量级框架
- hibernate 版本
Hibernate3.x
Hibernate4.x
Hibernate5.x(2017最新)
1.3 底层实现
1.3.1 ORM思想
- hibernate使用ORM思想对数据库进行CRUD操作
- 在web阶段学习Javabean更正确的叫法交:实体类
- ORM:Object Relational Mapping 对象关系映射
让 实体类和 数据库 表进行一一对应关系
让 实体类和 数据库 表进行对应
让 实体类 属性 和 表 里面 字段 对应
不需要操作数据库而是直接操作对应的实体类对象
2 Hibernate使用
2.1 入门
2.1.1 hibernate开发环境搭建
1)导入jar包
下载hibernate-release-5.2.10.Final
①压缩包中的lib中的required文件夹内的jar包
②jpa的jar包也在required内
③日志jar包.hibernate本身没有日志输出jar包,需导入其他日志输出jar包 log4j,slf4j-log4j,slf4j-api
④mysqk驱动jar包mysql-connector-java
2)创建实体Bean类
hibernate 要求有个实体类有一个属性唯一
(可以手动在mysql中创建表与bean类属性对应,也可以由hibernate自己创建)
package cn.wanxp.entity;
public class User {
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/*hibernate 要求有个实体类有一个属性唯一*/
private int uid;
private String username;
private String password;
private String address;
}
3) 配置实体类和数据库表一一对应关系(映射关系)
实用配置文件实现映射关系
(1) 创建xml格式的配置文件
映射文件的名称和位置没有固定要求
-- 建议在实体类的所在包内 实体类名称.hbm.xml
(2) 配置文件是xml格式,在配置文件中引入xml约束
-- hibernate里面引入的约束是dtd格式
(3)在包内创建映射文件User.hbm.xml
约束可以是本地文件在hibernate-release-5.2.10.Final内可搜索到,复制与User放一起然后写
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
也可以是网上公用的DTD文件,如果是使用网上的dtd约束则
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 上面这段约束可在hibernate-mapping-3.0.dtd中开头部分找到 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<!-- 1配置类和表对应
class标签
name 属性: 实体类全路径
table 属性:数据库名称(随意名称)
-->
<class name="cn.wanxp.entity.User" table="t_user">
<!-- 2配置实体类id和表id一一对应
要求两id在各自位置具有唯一性
-->
<!--
id标签
name属性: 实体类里面id属性名称
column属性:生成表字段名称(名称随意)
-->
<id name="uid" column="uid">
<!--设置数据表id增长策略
native 生成表id是主键和自增长
-->
<generator class="native"></generator>
</id>
<!-- 配置其他属性与字段对应
name属性:实体类属性名称
column属性:数据表字段名称
-->
<property name="username" column="username"></property>
<property name="password" column="password"></property>
<property name="address" column="address"></property>
</class>
</hibernate-mapping>
4)创建hibernate的核心配置文件
(1)核心配置文件格式xml,配置文件名称和位置固定
-位置: 必须src下面
-名称: 必须hibernate.cfg.xml
(2)引入约束
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
(3)hibernate 操作过程中只会加载核心配置文件,其它配置文件不会加载
第一部分:配置数据库信息
第二部分:配置hibernate信息
第三部分:映射文件放在核心配置文件中
注意
①方言
注意下面的这句:是MySQL5Dialect,不是properties文件中的MySQLDialect因为版本不一样了,
不然无法创建表。原因就是使用没有5的会用创建命令type=,而不是engine=
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
②字符集
-1- 如要使用字符集utf8,想要支持中文时:
先创建数据库database加上字符集:create database hibernate_day01 charset utf8 ;
然后在hibernate.cfg.xml加上下面标签属性,表示和数据库传递数据采用utf8编码
<property name="connection.useUnicode">true</property>
<property name="connection.characterEncoding">UTF-8</property>
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>
<!-- 第一部分:配置数据库信息
在下载的hibernate-release压缩包文件夹内搜索hibernate.properties打开后可看到对应的数据库需要配置的属性
-->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_day01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="connection.useUnicode">true</property>
<property name="connection.characterEncoding">UTF-8</property>
<!-- 第二部分:配置hibernate信息,可选的,可以配置也可以不用配置
同样能在hibernate.properties搜索到相应的值
-->
<!-- 如:输出底层sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 如:格式化底层sql语句 -->
<property name="hibernate.format_sql">true</property>
<!-- 如:hibernate创建表,需要配置后
update:如果已经存在表则更新,若不存在则创建
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置数据库方言
mysql里实现分页关键字 limit 只能在mysql里面
Oracle数据库,实现分页rownum
在hibernate框架识别不同数据库自己特有的语句
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 第三部分:配置映射文件 -->
<!-- resource引入路径根目录是src -->
<mapping resource="/cn/wanxp/entity/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
5)测试 添加操作 1-4,6,7写法基本都是固定的,变化的只有第5步
(1)加载hibernate核心配置文件
(2)创建SessionFactory对象
(3)使用SessionFactory创建session对象
(4)开启事务
(5)写具体逻辑
(6)提交事务
(7)关闭资源
示例如下
package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.wanxp.entity.User;
public class HibernateDemo {
@Test
public void testAdd() {
// (1)加载hibernate核心配置文件
//在hibernate里面封装对象
//将会到src下找到hibernate.cfg.xml的文件
Configuration cfg = new Configuration();
cfg.configure();
// (2)创建SessionFactory对象
//读取hibernate核心配置文件,创建SessionFactory
//过程中根据配置文件中的属性以及映射关系xml文件创建表
SessionFactory sessionFactory=cfg.buildSessionFactory();
// (3)使用SessionFactory创建session对象
//类似于JDBC连接
Session session = sessionFactory.openSession();
// (4)开启事务
Transaction tx = session.beginTransaction();
// (5)写具体逻辑编写CRUD操作
User user = new User();
String username="张三";
user.setUsername(username);
user.setPassword("password1");
user.setAddress("上海");
session.save(user);
// (6)提交事务
tx.commit();
// (7)关闭资源
session.close();
sessionFactory.close();
}
}
效果如下:
2 hibernate详解
2.1 hibernate配置文件
2.1.1 hibernate映射配置文件
- 映射文件没有固定位置
映射配置文件中,标签name属性写实体类中的属性名;column可以不用写,字段名就会命名为实体类属性同一名字。
property标签type属性可以设置数据表的字段属性。
2.1.2 核心配置文件
标签位置要求:
<hibernate-configuration>
<session-factory>
...
</session-factory>
</hibernate-configuration>
配置的三部分要求
- 数据库部分--必须
- hibernate部分--可选
- 映射文件--必须
2.2 hibernate核心api
2.2.1 Configuration
Configuration cfg = new Configuration();
cfg.configure();
上面代码的过程:从src下找到hibernate.cfg.xml文件并根据里面的配置,创建对象并加载
2.2.2 SessionFactory
- 根据配置文件中的属性创建表
<!-- 如:hibernate创建表,需要配置后
update:如果已经存在表则更新,若不存在则创建
-->
<property name="hibernate.hbm2ddl.auto">update</property>
- 创建SessionFactory过程,特别耗费资源,所以一般一个项目只创建一次
- 具体实现
(1)写工具类,写静态代码块
package cn.wanxp.utils;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
private static Configuration cfg = null;
private static SessionFactory sessionFactory = null;
static {
cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
2.2.3 Session
- Sesssion不同方法实现CRUD操作
(1) 添加:save
(2) 修改:update
(3) 删除:delete
(4) 根据id查询:get - session对象单线程对象
(1) session对象不能共用,只能自己使用
2.2.4 Transaction
- 事务对象
Transaction tx = session.beginTransaction();
- 事物提交和回滚方法
tx.commit();
tx.rollback();
- 事物
(1) 事物四个特性
原子性、一致性、隔离性、持久性
2.3 配置文件没有提示
复制下面的uri
http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd
Eclipse->Preferences->XML Catalog->Add->
location---找到hibernate-configuration-3.0.dtd
uri----http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd
这样就能不用联网就能使用http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd了
3 hibernate
3.1 实体类编写
3.1.1 编写规则
1) 私有域
2) set-get方法
3) 实体类有属性有唯一值
4) 实体类建议不使用基本类型,使用基本数据类型的包装类
(1) int--Integer,char--Character
(2) 比如表示学生分数用int就只能表示分数大小,不能使用null表示没参加,而是用Integer就能解决这个问题了
3.2 Hibernate主键生成策略
1) hibernate要求实体类厘米吗必须有一个属性作为唯一值,对应表主键,主键可以不同生成策略
2) hibernate主键生成策略有很多值
3) 在class里面的值
主要: native,uuid
UUID生成策略的实体类属性表id的类型是String类型
private String uid;
<generator>
3.3 Hibernate CRUD
3.3.1 增
User user = new User();
user.setUsername("李四");
user.setPassword("123456");
user.setAddress("上海");
session.save(user);
3.3.2 删
方法1 查询-删除
User user =session.get(User.class , 3);
session.delete(user);
方法2 设置-删除
User user = new User();
user.setId(3);
session.delete(user);
3.3.3 改
方法1
方法2 查询-修改
//李四换成王五
package cn.wanxp.hibernatetest;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;
public class HibernateDemoDay02 {
@Test
public void testAdd() {
// 直接调用SessionFactory调用静态调用方法
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
// (3)使用SessionFactory创建session对象
//类似于JDBC连接
Session session = sessionFactory.openSession();
// (4)开启事务
Transaction tx = session.beginTransaction();
// (5)写具体逻辑编写CRUD操作
User user = new User();
String username="李四";
user.setUsername(username);
user.setPassword("password1");
user.setAddress("上海");
session.save(user);
System.out.println(user);
user = session.get(User.class , 3);
user.setUsername("王五");
session.update(user);
user = session.get(User.class , 3);
System.out.println(user);
// (6)提交事务
tx.commit();
// (7)关闭资源
session.close();
sessionFactory.close();
}
}
3.3.4 查
User user = session.get(User.class,2);//查询并返回id=2的user
3.3.5 save,update,saveOrUpdate
save:id没有效果 ,只会直接添加
update:id的对应记录更新所有内容
3.4 实体类对象状态
3.4.1实体类状态三种状态
1)瞬时态,对象里面没有id值,对象与session没有关联
User user = new User();
user.setUsername("Jack");
user.setPassword("password");
user.setAddress("上海");
2)持久态,对象里有id值,与session相关联
User user = session.get(User.class , 3);
- 托管态,对象里有id值,与session没有关联
user.setId(3);
3.4.2演示操作实体类对象的方法
- saveOrUpdate方法:
- 实体类状态瞬时态,这个方法做的是添加操作
- 实体类状态托管态,这个方法做的是修改操作
- 实体类状态持久态,这个方法做的是修改操作
4 hibernate的优化
4.1 缓存
hibernate有两级缓存:一级缓存,二级缓存
4.1.1 一级缓存
1)第一类 hibernate一级缓存特点
(1)hibernate的一级缓存默认打开的
(2)hibernate的一级缓存适用范围,是session范围,从session创建到session关闭范围
(3)hibernate的一级缓存中,存储数据必须持久态数据
2)第二类hibernate的二级缓存
(1)目前 已经不适用了,替代技术redis
(2)二级缓存默认不是打开的,需要配置
(3)二级缓存使用范围,是SessionFactory范围
4.1.2 验证一级缓存的存在
1)验证方式
(1)首先根据uid=1查询,返回对象
(2)再次根据uid=1查询,返回对象
第二次查询将不会查询数据库而是直接拿出数据
4.1.3 一级缓存过程
session.get(),会先去一级缓存中查询,若没有则去数据库,若有直接拿出。
session范围是指从创建到close();
4.1.4 一级缓存的特点
持久态会自动执行
User user = session.get(User.class , 3);//将对象保存至一级缓存和快照区
user.setUsername("赵武"); //会同时修改:修改user对象里的值,修改持久态对象的值,修改一级缓存中的内容,不会修改快照区内容
tx.commit();//执行到这里即使没有update,也会修改数据库
//commit做的内容:提交事务,做的事情,比较一级缓存和快照区内容是否相同,若不同则将一级缓存内容写入数据库,若相同则不修改
效果图如下
4.2 事务详解
4.2.1 什么是事务
4.2.2 事务的特性
原子性,一致性,隔离性,持久性
4.2.3 不考虑隔离性产生的问题
- 脏读
2)不可重复读
3)虚读
4.2.4 设置事务隔离级别
1)mysql默认隔离级别 repeateable read
2)级别设置 在hibernate.cfg.xml中
<!--
1-Read uncommited isolation
2-Read commited isolation
4-Repeatable read isolation
8-Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
4.2.5 hibernate规范的事务写法
try{
//开启事务
//提交事务
}catch(){
//回滚事务
}final{
//关闭操作
}
@Test
public void testRollback() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
User user = session.get(User.class , 3);
user.setUsername("毛毛");
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally{
session.close();
sessionFactory.close();
}
}
4.3 hibernate绑定session线程
4.3.2 介绍
为了保证session是单线程的,而不会被其它人使用。
注意 由于与本地线程绑定,所以不需要关闭session了,因为本地线程一完成,session自动关闭
4.3.2 配置
- hibernate.cfg.xml中配置
<property name="hibernate.current_session_context_class">thread</property>
- 调用SessionFactory对象里的的方法得到
public static Session getSessionObject(){
return sessionFactory.getCurrentSession();
}
5 hibernate的api使用
5.1 查询
Query对象,Criteria对象 SQLQuery对象
5.1.1 Query对象
1)使用Query对象不需要写sql语句,但是写hql语句
(1)hql:hibernate query language,hibernate提供查询语句
这个hql语句和普通sql很相似
(2) hql和sql语句区别
sql操作表和字段
hql操作实体类和属性
2)查询所有hql语句:
(1)from实体类名称
3) Query对象使用
(1)创建 Query对象
(2)调用Query对象里的方法得到结果
Query query = session.createQuery("from User");
List<User> list = query.list();
5.1.2 Criteria对象
1) Query对象使用
(1)创建 Query对象
(2)调用Query对象里的方法得到结果
Criteria criteria = session.createCriteria(User.class);
List<User> list = query.list();
5.1.3 SQLQuery对象
- 使用SQL底层语句
2) SQLQuery对象使用
(1)创建 Query对象
(2)调用Query对象里的方法得到结果(默认是数组)
SQLQuer sqlQuery = session.createSQLQuery("select * from t_user where username='张三'");
//默认返回的数组
List list = query.list();
(3)调用Query对象里的方法得到结果为对象的做法
SQLQuer sqlQuery = session.createSQLQuery("select * from t_user where username='张三'");
sqlQuery.addEntity(User.class);
List<User> list = query.list();
6 hibernate的表关系操作
6.1 表的关系
1)一对多
一个分类有多个商品
2)多对多
订单与商品,一个订单多个商品,一个商品多个订单
3)一对一
丈夫与妻子
6.2 一对多
6.2.1 一对多表创建
1)创建“一”实体类并在里面创建“多”的Set(集合)如HashSet
2)创建“多”实体类并在里面创建“一”的实体的对象作为私有域
3)创建“一”实体类映射表,将基本的私有域建立好映射
4)创建“多”实体类映射表,将基本的私有域建立好映射
5)在配置文件中配置一对多关系
6)创建核心配置文件hibernate.cfg.xml
下面是一个例子,客户(企业)类,每个(客户)企业都有很多员工的联系方式,客户表-员工联系方式表就是一对多的关系
①----客户实体类 Customer.java
package cn.wanxp.entity;
import java.util.HashSet;
import java.util.Set;
public class Customer {
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerLevel() {
return customerLevel;
}
public void setCustomerLevel(String customerLevel) {
this.customerLevel = customerLevel;
}
public String getCustomerSource() {
return customerSource;
}
public void setCustomerSource(String customerSource) {
this.customerSource = customerSource;
}
public String getCustomerPhone() {
return customerPhone;
}
public void setCustomerPhone(String customerPhone) {
this.customerPhone = customerPhone;
}
public String getCustomerMobile() {
return customerMobile;
}
public void setCustomerMobile(String customerMobile) {
this.customerMobile = customerMobile;
}
//在客户实体类中表示多个联系人,一个客户对应多个联系人
//hibernate要求使用集合表示多的数据,使用Set集合
private Set<Linkman> setLinkman = new HashSet<Linkman>();
public Set<Linkman> getSetLinkman() {
return setLinkman;
}
public void setSetLinkman(Set<Linkman> setLinkman) {
this.setLinkman = setLinkman;
}
private Integer cid;
private String customerName;
private String customerLevel;
private String customerSource;
private String customerPhone;
private String customerMobile;
}
②----”多“的表实体类Linkman.java
package cn.wanxp.entity;
public class Linkman {
public Integer getLid() {
return lid;
}
public void setLid(Integer lid) {
this.lid = lid;
}
public String getLinkmanName() {
return linkmanName;
}
public void setLinkmanName(String linkmanName) {
this.linkmanName = linkmanName;
}
public String getLinkmanGender() {
return linkmanGender;
}
public void setLinkmanGender(String linkmanGender) {
this.linkmanGender = linkmanGender;
}
public String getLinkmanPhone() {
return linkmanPhone;
}
public void setLinkmanPhone(String linkmanPhone) {
this.linkmanPhone = linkmanPhone;
}
//在联系人实体类里表示所属客户,一个联系人属于一个客户
private Customer customer = new Customer();
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
private Integer lid;
private String linkmanName;
private String linkmanGender;
private String linkmanPhone;
}
③----Customer实体配置文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="cn.wanxp.entity.Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="customerName" column="customerName"></property>
<property name="customerLevel" column="customerLevel"></property>
<property name="customerSource" column="customerSource"></property>
<property name="customerPhone" column="customerPhone"></property>
<property name="customerMobile" column="customerMobile"></property>
<!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人
set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
-->
<set name="setLinkman">
<!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键
column属性值:外键名称
-->
<key column="cid"></key>
<one-to-many class="cn.wanxp.entity.Linkman"/>
</set>
</class>
</hibernate-mapping>
④----Linkman配置文件 Linkman.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="cn.wanxp.entity.Linkman" table="t_linkman">
<id name="lid" column="lid">
<generator class="native"></generator>
</id>
<property name="linkmanName" column="linkmanName"></property>
<property name="linkmanGender" column="linkmanGender"></property>
<property name="linkmanPhone" column="linkmanPhone"></property>
<!-- 在联系人中配置所属客户
name属性:因为在联系人实体类使用Customer对象表示。写customer名称
class属性:Customer全路径
column属性是生成的表格存储Customer的id的列名称要与Customer.hbm.xml中key一致
-->
<many-to-one name="customer" class="cn.wanxp.entity.Customer" column="cid"></many-to-one>
</class>
</hibernate-mapping>
⑤----同样需要将配置文件加入到hibernate核心配置文件中
<mapping resource="/cn/wanxp/entity/Customer.hbm.xml" />
<mapping resource="/cn/wanxp/entity/Linkman.hbm.xml" />
⑥----测试效果
package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.wanxp.entity.User;
public class HibernateDemoDay03 {
@Test
public void testOneToMany() {
Configuration cfg = new Configuration();
cfg.configure();
SessionFactory sessionFactory=cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
tx.commit();
session.close();
sessionFactory.close();
}
}
⑦----输出结果
Hibernate:
create table t_customer (
cid integer not null auto_increment,
customerName varchar(255),
customerLevel varchar(255),
customerSource varchar(255),
customerPhone varchar(255),
customerMobile varchar(255),
primary key (cid)
) engine=MyISAM
Hibernate:
create table t_linkman (
lid integer not null auto_increment,
linkmanName varchar(255),
linkmanGender varchar(255),
linkmanPhone varchar(255),
cid integer,
primary key (lid)
) engine=MyISAM
Hibernate:
alter table t_linkman
add constraint FKn4dw5i2knad5dl95xit4v1b0p
foreign key (cid)
references t_customer (cid)
6.2.1 一对多表操作
1)一对多级联保存 -- 复杂写法
直接在session中设置并保存
package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.wanxp.entity.Customer;
import cn.wanxp.entity.Linkman;
import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;
public class HibernateDemoDay03 {
@Test
public void testOneToManyAdd() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
Customer customer = new Customer();
Linkman linkman = new Linkman();
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
//1创建客户和联系人对象并设置基本域值
linkman.setLinkmanName("李四");
linkman.setLinkmanGender("男");
linkman.setLinkmanPhone("131313");
customer.setCustomerName("联想");
customer.setCustomerSource("web");
customer.setCustomerLevel("vip");
customer.setCustomerPhone("3333");
customer.setCustomerMobile("1111");
//2在客户表中和联系人表中连接部分分别添加对方
linkman.setCustomer(customer);
customer.getSetLinkman().add(linkman);
//3使用save存储进数据表中
session.save(customer);
session.save(linkman);
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
session.close();
sessionFactory.close();
}
}
}
2)一对多级联保存 -- 简单写法
第一步在客户映射文件中进行配置
修改Customer.hbm.xml配置文件中的set标签,在set标签中添加 cascade="save-update"属性
<set name="setLinkman" cascade="save-update">
上面的HibernateDemoDay03.java就可以修改为
package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.wanxp.entity.Customer;
import cn.wanxp.entity.Linkman;
import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;
public class HibernateDemoDay03 {
@Test
public void testOneToManyAdd() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
Customer customer = new Customer();
Linkman linkman = new Linkman();
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
//1创建客户和联系人对象并设置基本域值
linkman.setLinkmanName("李四");
linkman.setLinkmanGender("男");
linkman.setLinkmanPhone("131313");
customer.setCustomerName("联想");
customer.setCustomerSource("web");
customer.setCustomerLevel("vip");
customer.setCustomerPhone("3333");
customer.setCustomerMobile("1111");
//2在客户表中添加联系人实体类对象,相互
linkman.setCustomer(customer);
customer.getSetLinkman().add(linkman);
//3使用save存储进数据表中
session.save(customer);
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
session.close();
sessionFactory.close();
}
}
}
效果如下
3)一对多删除
如,删除某个客户,把客户里面所有的联系人删除
① 在客户映射文件中进行配置(多个值用逗号隔开)
修改Customer.hbm.xml配置文件中的set标签,在set标签中添加 cascade="save-update,delete"属性
<set name="setLinkman" cascade="save-update,delete">
② 在代码中直接删除
先根据id查找客户然后删除
@Test
public void testOneToManyDelete() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
Customer customer = new Customer();
Linkman linkman = new Linkman();
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
customer = session.get(Customer.class, 1);
session.delete(customer);
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
session.close();
sessionFactory.close();
}
}
效果如下
3)一对多修改
(1)如将多方的ABC三个记录中的C的外键Y修改为一方的XY中的X
Customer X=session.get(Customer.class,1);
Linkman C=session.get(Linkman.class,3);
X.getSetLinkman().add(C);
(2)一对多的里面,可以让其中一方放弃维护,用以提高效率。一般让一放弃维护,多的维护
(3)具体实现放弃维护
set标签中的inverse属性:放弃true,不放弃false(默认)
<set name="setLinkman" inverse="true" cascade="save-update,delete">
6.2.2 多对多表
- 多对多映射配置
(1)创建实体类,用户和角色
Person.java
package cn.wanxp.entity;
import java.util.HashSet;
import java.util.Set;
public class Person {
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Role> getSetRole() {
return setRole;
}
public void setSetRole(Set<Role> setRole) {
this.setRole = setRole;
}
private Integer pid;
private String name;
private Set<Role> setRole = new HashSet<Role>();
}
Role.java
package cn.wanxp.entity;
import java.util.HashSet;
import java.util.Set;
public class Role {
public Integer getRid() {
return rid;
}
public void setRid(Integer rid) {
this.rid = rid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Person> getSetPerson() {
return setPerson;
}
public void setSetPerson(Set<Person> setPerson) {
this.setPerson = setPerson;
}
private Integer rid;
private String name;
private Set<Person> setPerson = new HashSet<Person>();
}
(2)让两个实体类之间相互表示
①用户里表示所有角色,使用set集合
②一个角色有多个用户,使用set集合
(3)配置映射
①基本配置
②配置多对多关系
person.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="cn.wanxp.entity.Person" table="t_person">
<id name="pid" column="pid">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人
set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
table:第三张表名
-->
<set name="setRole" table="t_person_role" cascade="all">
<!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键
key属性 column当前映射文件在第三张表的外键名称
-->
<key column="person_id"></key>
<!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键
many-to-many属性 column值Role在第三张表的外键名称
-->
<many-to-many class="cn.wanxp.entity.Role" column="role_id" />
</set>
</class>
</hibernate-mapping>
role.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="cn.wanxp.entity.Role" table="t_role">
<id name="rid" column="rid">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人
set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
-->
<set name="setPerson" table="t_person_role" cascade="all">
<!--
key属性 column当前映射文件在第三张表的外键名称
-->
<key column="role_id"></key>
<!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键
many-to-many属性 column值Person在第三张表的外键名称
-->
<many-to-many class="cn.wanxp.entity.Person" column="person_id"/>
</set>
</class>
</hibernate-mapping>
--在用户里表示所有角色,使用set标签
--在角色里表示所有用户,使用set标签
(4)核心配置中引入配置文件
2)多对多级联保存
public void testManyToManyAdd() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
//创建对象
Person p1 = new Person();
Person p2 = new Person();
p1.setName("张三");
p2.setName("李四");
Role r1 = new Role();
Role r2 = new Role();
Role r3 = new Role();
r1.setName("肉坦");
r2.setName("输出");
r3.setName("辅助");
//关联对象
p1.getSetRole().add(r1);
p2.getSetRole().add(r2);
p1.getSetRole().add(r3);
//保存对象
session.save(p1);
session.save(p2);
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
session.close();
sessionFactory.close();
}
}
效果如下
3)多对多级联删除
删除方式同一对多删除
4)维护第三张表关系
用户表添加角色 搜索用户表,搜索角色表,在用户表中添加角色,如p1.getSetRole().add(r5);
用户表删除角色 搜索用户表,搜索角色表,在用户表中删除角色,如p1.getSetRole().remove(r2);
7 hibernate 查询方式
7.1 对象导航查询
如一对多表格 客户与联系人,根据某个id查询 客户以及下面的联系人
先按照id搜索客户,然后获取set集合即可
Customer customer = session.get(Customer.class , 1);
Set<Linkman> setLinkman = customer.getSetlinkman();
7.2 OID查询
根据id查询某一条记录
按照id搜索客户
Customer customer = session.get(Customer.class , 1);
7.3 hql查询
Query对象,写hql语句查询
hql与sql类似
区别:hql操作实体类和属性,sql操作数据库表和字段
过程:创建Query对象,写hql语句 ,调用Query对象的方法获得结果
7.3.1 查询所有
//from 实体类名称
Query query = session.createQuery("from Customer");
//调用方法得到结果
List<Customer> list = query.list();
7.3.2 条件查询
//from 实体类名称 where 实体类属性名称=? [and 实体类属性名称=? ...] 或者 from 实体类名称 实体类属性名称 like ?
Query query = session.createQuery("from Customer c where c.cid=? and c.customerName=?");
//问好问号位置从0开始
query.setParameter(0,1);
query.setParameter(1,"百度");
//调用方法得到结果
List<Customer> list = query.list();
7.3.3 排序查询
//在上面基础上 加入 order by 实体类属性名称 desc|asc (desc 降序,asc 升序)
Query query = session.createQuery("from Customer c where c.cid=? and c.customerName=?" order by c.cid desc);
7.3.4 分页查询
Query query = session.createQuery("from Customer");
//设置起始位置
query.setFirstResult(0);
//设置每页记录数量
query.setMaxResult(3);
//调用方法得到结果
List<Customer> list = query.list();
7.3.5 投影查询
sql:select customerName from Customer; --查询部分字段而不是所有字段
Query query = session.createQuery("select customerName from Customer");
list<Object> list = query.list();//注意这里的区别
7.3.6 聚集函数使用
常用count sum avg max min
//hql:select count(*) from 实体类名
Query query = session.createQuery(" select count(*) from Customer");
Object obj = query.uniqueResult();
7.4 QBC查询
1)使用hql需要写hql语句,但使用qbc是不需要写语句,只需要方法就行
2)使用qbc操作实体类和属性
3)使用Criteria对象进行操作
7.4.1 查询所有
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法
List<Customer> list = criteria.list();
7.4.2 条件查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件
//add方法添加条件,参数Restrictions对象的方法,方法里传入实体类属性和值
criteria.add(Restrictions.eq("cid",1));
List<Customer> list = criteria.list();
7.4.3 排序查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件
//addOrder方法添加条件,参数Order对象的方法,方法里传入实体类属性asc升序desc降序
criteria.addOrder(Order.asc("cid"));
List<Customer> list = criteria.list();
7.4.4 分页查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件
//setFirstResult(0)起始位置0,setMaxResult(3)获得3条记录
criteria.setFirstResult(0);
criteria.setMaxResult(3);
List<Customer> list = criteria.list();
7.4.5 统计查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件
criteria.setProjection(Projections.rowCount());
Object obj = criteria.uniqueResult();
Long lobj = (Long) obj
int count = lobj.intValue();
System.out.println(count);
7.4.5 离线查询
//创建DetachedCriteria对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
7.5 本地sql查询
SQLQuery对象,使用普通sql实现
8 多表查询
8.1 内连接
8.1.1 内连接
sql语句:select * from t_customer as c inner join t_linkman as l on c.cid=l.lid;
Query query = session.createQuery("from Customer c inner join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.1.2 迫切内连接
1)与内连接底层是一样的
2)区别:使用内连接返回list中每部分是数组,迫切内连接返回的是对象
用法:加入关键字fetch
Query query = session.createQuery("from Customer c inner join fetch c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.2 左外连接
8.2.1 左外连接
sql语句:select * from t_customer as c left outer join t_linkman as l on c.cid=l.lid;
Query query = session.createQuery("from Customer c left outer join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.2.2 迫切左外连接
效果用法区别与内连接类似
8.3 右外连接
8.3.1 右外连接
sql语句:select * from t_customer as c right outer join t_linkman as l on c.cid=l.lid;
Query query = session.createQuery("from Customer c right outer join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.3.2 迫切右外连接
效果用法区别与内连接类似
9 hibernate检索策略
9.1 立即查询
如根据id查询,调用get方法时,一调用get方法立即发送查询语句给数据库
9.2 延迟查询
如根据id查询,调用load方法时,调用load方法,不会立即发送查询语句给数据库,只有得到对象里面的值时才会发送查询语句
分类:类级别延迟,关联级别延迟
9.2.1 类级别延迟
就是上面那种
9.2.2 关联类级别延迟
如查询Customer时,是否延迟查询关联的Linkman
默认状态
Customer customer = session.get(Customer.class , 1);
//执行到这里时,只发送了查询表t_customer
Set<Linkman> setLinkman = customer.getSetlinkman();
//执行到这里时,只发送了查询表t_customer
System.out.println(list.size());//执行到这里将会发送查询t_linkman表命令
修改延迟
在set标签上修改属性
fetch :值select
lazy:值
----true:延迟,默认
----false:不延迟
----extra:极其延迟,要什么值就去查什么值
10 批量查询
如获取所有客户的联系人
方式1 普通方式,效率较低
//下面这种方式经过测试效率不高,因为每次查询一条客户再查询联系人都要发送查询命令
Criteria criteria =session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for(Customer customer:list){
Systerm.out.println(customer.getId());
Set setLinkman =customer.getSetLinkman();
for(Linkman linkman : setLinkman){
System.out.println(linkman.getId());
}
}
方式2 修改配置Customer文件 ,在set标签中加入属性batch-size,batch-size的值只要是整数即可,其值越大,发送的语句命令越少,效率越高
<set name = "setLinkman" batch-size="2">