Hibernate入门
一、概述
- hibernate概述:开源的对象关系映射(ORM)框架。它对JDBC进行了非常轻量级的封装,它可以自动生成SQL语句,自动执行,可以让我们使用对象编程思想来操作数据库。持久层框架。注意点:ORM:object relational mapping(Java对象与关系型数据库中的表建立一种映射关系)
- 我们使用的hibernate版本是:5x;解压下载的hibernate开发包会得到三个文件夹:
①documentation—开发的文档
②lib----开发包(required—必须的依赖包、optional-----可选的jar包)
③project----提供的项目
二、入门小案例
2.1创建项目引入jar包
①数据库的驱动包:
②hibernate开发必须包:
③日志记录包:
2.2 创建表
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
这里需要注意的是:列名包含的那个符号是esc下面那个键,而不是单引号
2.3创建实体类
2.4创建映射配置文件*
<?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="com.itheima.hibernate.demo1.Customer" table="cst_customer">
<!-- 建立类中的属性与表的主键对应 -->
<id name="cust_id" column="cust_id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<!-- 建立类的普通属性与表的字段的对应 -->
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobil" column="cust_mobil"/>
</class>
</hibernate-mapping>
注意点:
①映射文件的约束的位置在hibernate-configuration-3.0.dtd里面寻找
②映射文件的命名格式:类名.hbm.xml
总结:
①【class标签的配置】建立类与表的映射关系
属性:
name:类的全路径
table:表名(类名与表名一致的时候可以省略)
catalog:数据库名
②【id标签的配置】建立类中的属性与表的主键的对应关系
属性:
name:类的属性名
column:表的字段名(类中属性名与表中字段名一致可以省略column)
length:长度
type:类型
③【property标签的配置】建立类中普通属性与表的字段的对应关系
属性:
name:类中属性名
column:表中的字段名
length:长度
type:类型
not-null:非空
unique:设置唯一
2.5创建核心的配置文件
<?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.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///repeathibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 配置hibernate的方言 =====================-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 可选的配置============================== -->
<!-- 是否让控制台打印sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 是否格式化sql语句 -->
<property name="hibernate.format_sql">true</property>
<!-- 是否让hibernate自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置C3P0连接池============================= -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>
<!-- 引入映射文件 =====================================-->
<mapping resource="com/itheima/hibernate/demo1/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
注意点:
①约束的位置在libraries/core.jar最后几个文件
②数据库连接的配置信息在hibernate开发包提供的项目案例里面(etc)
③hibernate只会读取核心配置文件,所以需要将映射文件包含在核心配置里面
总结:
①必须的配置
【连接数据库的基本参数】驱动类,URL路径,用户名,密码
【方言】
②可选的配置
显示sql:hibernate.show_sql
格式化sql:hibernate.format_sql
自动建表:hibernate.hbm2ddl.auto,具体参数如下:
none | 不使用hibernate的自动建表 |
---|---|
create | 如果数据库中已经有表,删除原有表,重新创建;如果没有表就新建一个表(主要用来进行测试) |
create-drop | 如果数据库中已有表,删除原有表,执行操作,删除这个表;如果没有表,新建一个表,使用结束删除该表(测试) |
update | 如果数据库中有表,使用原有表;如果没有表,创建新表(更新表结构) |
validate | 如果没有表,不会创建表;只会使用数据库中的原有表(校验映射和表结构) |
③映射文件的引入,使用标签mapping 文件路径放在resource标签里面
2.6编写测试代码
public class HibernateTest {
@Test
public void demo1(){
//加载核心配置文件
Configuration configuration = new Configuration().configure();
//创建一个sessionFactory对象,类似于JDBC连接池
SessionFactory sessionFactory = configuration.buildSessionFactory();
//通过SessionFactory对象获取Session对象,类似于JDBC中的Connection
Session session = sessionFactory.openSession();
//手动开启事务
Transaction transaction = session.beginTransaction();
//编写代码
Customer customer = new Customer();
customer.setCust_name("小豹子");
customer.setCust_level("小弟");
session.save(customer);
//提交事务
transaction.commit();
//资源关闭
session.close();
}
}
三、hibernate的核心API
3.1Configuration:hibernate的配置对象
①加载核心配置文件
【hibernate.properties】
Congfiguration cfg = new Configuration();
【hibernate.hbm.xml】
Configuration cfg = new Configuration().configure();
②加载映射文件
手动加载:
configuration.addResource("com/ithiema/hibernate/demo1/Customer.hbm.xml");
3.2SessionFactory:Session工厂
描述:SessionFactory接口负责初始化hibernate。充当数据存储源的代理,负责创建session对象。这里用到了工厂模式。SessionFactory不是轻量级的,一个项目通常只需要一个SessionFactory就可以了,用到多个数据库,可以为每个数据库指定一个SessionFactory。
SessionFactory内部维护了Hibernate连接池和Hibernate的二级缓存。是线程安全的对象,一个项目创建一个对象就可以了。
抽取工具类:
package com.itheima.hibernate.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
public static Configuration configure;
public static SessionFactory sessionFactory;
static {
configure = new Configuration().configure();
sessionFactory = configure.buildSessionFactory();
}
public static Session openSession(){
return sessionFactory.openSession();
}
}
3.3Session:类似connection是连接对象
Session接口负责执行被持久化对象的CRUD操作,Session对象是非线程安全的,与数据库交流的桥梁。
Session中的API:
①保存方法:
public void demo1(){
//加载核心配置文件
Configuration configuration = new Configuration().configure();
//创建一个sessionFactory对象,类似于JDBC连接池
SessionFactory sessionFactory = configuration.buildSessionFactory();
//通过SessionFactory对象获取Session对象,类似于JDBC中的Connection
Session session = sessionFactory.openSession();
//手动开启事务
Transaction transaction = session.beginTransaction();
//编写代码
Customer customer = new Customer();
customer.setCust_name("小豹子");
customer.setCust_level("小弟");
session.save(customer);
//提交事务
transaction.commit();
//资源关闭
session.close();
}
②查询方法
【T get(Class c,Serializable id)】
public void demo3(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = session.get(Customer.class, 2l);
System.out.println(customer);
transaction.commit();
session.close();
}
【T load(Class c ,Serializable id)】
public void demo2(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = session.load(Customer.class, 1l);
System.out.println(customer);
transaction.commit();
session.close();
}
load方法与get方法的区别:
get采用的是立即加载,执行到这行代码的时候,就会马上发送SQL语句去查询
查询后返回的是真实对象本身
查询一个找不到的对象的时候返回的是null
load采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用到这个对象的时候才会发送SQL语句
查询后返回的是代理对象
查询一个找不到的对象的时候,返回的是ObjectNotFoundException
③修改方法(先查询再修改)
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = session.get(Customer.class, 1l);
customer.setCust_name("小煤球");
session.update(customer);
transaction.commit();
session.close();
}
④删除方法(先查询再删除)
public void demo5(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = session.get(Customer.class, 2l);
session.delete(customer);
transaction.commit();
session.close();
}
⑤保存或更新方法(void saveOrUpdate(Object obj))
public void demo6(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = new Customer();
customer.setCust_name("小茗子");
customer.setCust_source("小豹子推荐");
session.saveOrUpdate(customer);
transaction.commit();
session.close();
}
⑥查询所有
【面向对象查询:HQL】
public void demo7(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
}
【接收SQL】
public void demo8(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
SQLQuery query = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
transaction.commit();
session.close();
}
3.4事务对象:Transaction
begin()
commit()
rollback()
四、持久化类
4.1持久化类的编写规则
持久化:将内存中的一个对象持久化到数据库的过程,hibernate就是用来进行持久化的框架
持久化类:一个Java对象与数据库中的表建立了映射关系,这个类在hibernate中就被称为持久化类。
规则 | 描述 |
---|---|
对持久化类提供一个无参数的构造方法 | hibernate底层需要使用反射生成实例 |
属性需要私有,对私有属性提供public的get和set方法 | hibernate中获取与设置对象的值 |
对持久化类提供一个唯一标识OID与数据库的主键对应 | Java中通过对象的地址区分是否是同一个对象;数据库中通过主键确定是否是同一个记录,在hibernate中通过持久化类的OID的属性区分是否是同一个对象 |
持久化类的属性尽量使用包装类类型 | 因为基本数据类型默认值是0,0会产生很多歧义。包装类类型的默认值是null |
持久化类不要使用final修饰 | 延迟加载本身是hibernate的一个优化手段,返回的是一个代理对象(javassist可以对没有实现接口的类产生代理,使用了非常底层的字节码增强技术,继承这个类进行代理),如果不能被继承,不能产生代理对象,延迟加载也就失效,load方法与get方法也就别无二致了。 |
4.2持久化类的三种状态
hibernate是持久层框架,通过持久化类完成ORM操作。hibernate为了更好的管理持久化类,将持久化类分成三种状态
持久化类 = Java类+映射文件
持久化类的三种状态:
状态 | 描述 |
---|---|
瞬时态 | 这种对象没有唯一标识OID,没有被sesssion管理 |
持久态 | 这种对象有唯一标识OID,被session管理(持久化类的持久态对象,可以自动更新数据库) |
脱管态 | 有唯一标识,没有被session管理 |
4.3持久化类三种状态的转换(了解)
①瞬时态对象:
获取:Customer customer = new Customer();
状态转换:
瞬时态→持久态 save()、saveOrUpdate()
瞬时态→脱管态 customer.setCust_id(1)
②持久态对象:
获得:get()、load()、find()、iterate()
状态转换:
持久态→瞬时态 delete()
持久态→脱管态 close()、clear()、evict()
③脱管态对象:
获得:Customer customer = new Customer();
customer.setCust_id(1);
状态转换:
脱管态→持久态:update()、saveOrUpdate()
脱管态→瞬时态:customer.setCust_id(null);
五、主键生成策略
5.1主键的分类
①自然主键:主键本身就是表中的一个字段(实体中一个具体属性)
如:创建一个人员表,人员都会有个身份证号(唯一的不可重复的),使用了身份证号作为主键,这些主键称为自然主键。
②代理主键:主键本身不是表中的一个字段(不是实体中某一个具体的属性)
如:创建一个人员表,没有使用人员中的身份证号,用一个与这个表不相关的字段ID,这种主键称为代理主键
在实际开发中,尽量使用代理主键:
一旦自然主键参与到逻辑中,后期有可能修改源代码
好的程序设计满足OCP原则。对程序的扩展是open的,对修改源码是close的
5.2主键的生成策略
在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置,在hibernate中为了减少程序编写,提供了很多种主键的生成策略。
方式 | 描述 |
---|---|
increment | hibernate中提供的自动增长机制,适用于short、int、long类型的主键,在单线程的程序中使用(首先发送一条语句:select max(id) from 表,然后让id+1作为下一条记录的主键) |
identity | 适用于short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增长机制的数据库(MySQL、MSSQL,但是Oracle没有自动增长机制) |
sequence | 适用于short、int、long类型的主键,采用的序列的方式(oracle支持序列)MYSQL不支持序列的方式 |
uuid | 适用于字符串类型主键,使用hibernate中随机方式生成字符串主键 |
native | 本地策略,可以在identity和sequence中随意切换 |
assigned | hibernate放弃外键的管理,需要手动编写程序或者用户自己设置 |
foreign | 外部的。一对一的关联映射的情况下使用 |
六、缓存
6.1概述
缓存:一种优化方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
hibernate 一级缓存:hibernate提供了优化手段:缓存、抓取策略。hibernate提供了两种缓存机制,hibernate的一级缓存,称为是session级别的缓存,一级缓存生命周期与session一致(一级缓存是由session中的一系列Java集合构成的),一级缓存是自带的不可卸载的(hibernate的二级缓存是SessionFactory级别的缓存,需要配置的缓存)
七、事务
7.1概述
事务:事务是指逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功要么全部失败
7.2事务的特性
特性 | 描述 |
---|---|
原子性 | 代表事务不可分割 |
一致性 | 事务执行前后,事务的完整性保持一致 |
隔离性 | 代表事务执行的过程中,不应该受到其他事务的干扰 |
持久性 | 代表事务执行完成后,数据就持久到数据库中 |
7.3如果不考虑隔离性,会引发安全性问题
读问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务未提交的数据 |
不可重复读 | 一个事务读到另一个事务已经提交的update数据,导致前一个事务多次查询的结果不一致 |
虚读(幻读) | 一个事务读到另一个事务已经提交的insert数据,导致前一个事务多次查询的结果不一致 |
写问题:引发两类丢失更新
7.4读问题的解决
设置事务的隔离级别
隔离级别 | 能解决的问题 |
---|---|
Read uncommitted1 | 以上读问题都会发送 |
Read committed2 | 解决脏读,但是不可重复读和虚读有可能发生 |
Repeatable read4 | 解决脏读和不可重复读,但是虚读有可能发生 |
Serializable8 | 解决所有问题 |
在hibernate如何设置事务的隔离级别
<property name="hibernate.connection.isolation">4</property>
线程绑定的Session:
<property name="hibernate.current_session_context_class">thread</property>
八、一对多案例
8.1 建表原则
关系 | 建表原则 |
---|---|
一对多 | 在多的一方创建外键指向一的一方的主键 |
多对多 | 创建中间表,中间表至少有两个字段分别作为外键指向多对多双方的主键 |
8.2创建实体类
【客户类】:
package com.itheima.hibernate.domain;
import java.util.HashSet;
import java.util.Set;
public class Customer {
private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_phone;
private String cust_mobile;
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
public Long getCust_id() {
return cust_id;
}
public void setCust_id(Long cust_id) {
this.cust_id = cust_id;
}
public String getCust_name() {
return cust_name;
}
public void setCust_name(String cust_name) {
this.cust_name = cust_name;
}
public String getCust_source() {
return cust_source;
}
public void setCust_source(String cust_source) {
this.cust_source = cust_source;
}
public String getCust_industry() {
return cust_industry;
}
public void setCust_industry(String cust_industry) {
this.cust_industry = cust_industry;
}
public String getCust_level() {
return cust_level;
}
public void setCust_level(String cust_level) {
this.cust_level = cust_level;
}
public String getCust_phone() {
return cust_phone;
}
public void setCust_phone(String cust_phone) {
this.cust_phone = cust_phone;
}
public String getCust_mobile() {
return cust_mobile;
}
public void setCust_mobile(String cust_mobile) {
this.cust_mobile = cust_mobile;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
}
【联系人类】:
package com.itheima.hibernate.domain;
public class LinkMan {
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
private Customer customer;
public Long getLkm_id() {
return lkm_id;
}
public void setLkm_id(Long lkm_id) {
this.lkm_id = lkm_id;
}
public String getLkm_name() {
return lkm_name;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public String getLkm_gender() {
return lkm_gender;
}
public void setLkm_gender(String lkm_gender) {
this.lkm_gender = lkm_gender;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
}
public String getLkm_mobile() {
return lkm_mobile;
}
public void setLkm_mobile(String lkm_mobile) {
this.lkm_mobile = lkm_mobile;
}
public String getLkm_email() {
return lkm_email;
}
public void setLkm_email(String lkm_email) {
this.lkm_email = lkm_email;
}
public String getLkm_qq() {
return lkm_qq;
}
public void setLkm_qq(String lkm_qq) {
this.lkm_qq = lkm_qq;
}
public String getLkm_position() {
return lkm_position;
}
public void setLkm_position(String lkm_position) {
this.lkm_position = lkm_position;
}
public String getLkm_memo() {
return lkm_memo;
}
public void setLkm_memo(String lkm_memo) {
this.lkm_memo = lkm_memo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
8.3创建数据库及表
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
效果图:
【客户】
【联系人】
两个表的联系示意图:
8.4创建映射文件
【客户】
<?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="com.itheima.hibernate.domain.Customer" table="cst_customer">
<!-- 建立OID与主键的映射关系 -->
<id name="cust_id" column="cust_id">
<generator class="native"/>
</id>
<!-- 普通属性与表的字段对应 -->
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
<!-- 配置联系人 -->
<set name="linkMans" cascade="save-update,delete">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 多的一方类的全路径 -->
<one-to-many class="com.itheima.hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
【联系人】
<?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="com.itheima.hibernate.domain.LinkMan" table="cst_linkman">
<id name="lkm_id" column="lkm_id">
<generator class="native"></generator>
</id>
<property name="lkm_name" column="lkm_name"></property>
<property name="lkm_gender" column="lkm_gender"></property>
<property name="lkm_phone" column="lkm_phone"></property>
<property name="lkm_mobile" column="lkm_mobile"></property>
<property name="lkm_email" column="lkm_email"></property>
<property name="lkm_qq" column="lkm_qq"></property>
<property name="lkm_position" column="lkm_position"></property>
<property name="lkm_memo" column="lkm_memo"></property>
<!-- 配置客户 -->
<many-to-one name="customer" cascade="save-update,delete" column="lkm_cust_id" class="com.itheima.hibernate.domain.Customer"></many-to-one>
</class>
</hibernate-mapping>
8.5一对多测试1
①创建两个客户和三个联系人
//创建两个客户和三个联系人并将他们联系起来
public void test1(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
//创建两个客户
Customer customer1 = new Customer();
customer1.setCust_name("罗甲");
Customer customer2 = new Customer();
customer2.setCust_name("罗乙");
//创建三个联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("罗A");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("罗B");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("罗C");
//设置关系
linkMan1.setCustomer(customer1);
linkMan2.setCustomer(customer1);
linkMan3.setCustomer(customer2);
customer1.getLinkMans().add(linkMan1);
customer1.getLinkMans().add(linkMan2);
customer2.getLinkMans().add(linkMan3);
//保存
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
session.save(customer1);
session.save(customer2);
transaction.commit();
}
②一对多只保存一边是否可以?
//只保存一边是否可行
public void test2(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = new Customer();
customer.setCust_name("大哥");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小弟");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
session.save(customer);
transaction.commit();
}
这样是不可以的,会出现异常,无法保存
8.6级联操作
级联:操作一个对象的时候是否会操作其关联的对象
级联的方向性:
操作一的一方的时候,是否会操作到多的一方。
操作多的一方的时候,是否会操作到一的一方。
①级联保存或更新
【保存客户级联联系人】需要在客户的映射文件里面配置一下级联信息!
现在下面这个保存一边就不会出现异常了!
public void test3(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = new Customer();
customer.setCust_name("大哥");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小弟");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
session.save(customer);
transaction.commit();
}
【保存联系人级联客户】需要在联系人的映射文件中配置级联信息!
下面这个就是保存另外一边!
//保存联系人级联客户
public void test4(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = new Customer();
customer.setCust_name("大哥哥");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小弟弟");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
session.save(linkMan);
transaction.commit();
}
②级联删除
【删除客户级联删除联系人】:
如果没有在客户的映射文件中去配置级联信息,删除的时候,可以删除客户,但是不能删除联系人,联系人的外键会修改为null;
如果设置了级联信息,删除客户会同时删除联系人。
public void test5(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Customer customer = session.get(Customer.class, 9l);
session.delete(customer);
transaction.commit();
}
【删除联系人级联删除客户】(基本不用)
8.7一对多设置了双向关联,产生了多余的SQL语句
解决办法:
①单向维护
②使一方放弃外键维护权(一的一方放弃,在set上设置inverse=“true”)
九、多对多关联映射
9.1创建表
①用户表:
CREATE TABLE `sys_user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_code` varchar(32) NOT NULL COMMENT '用户账号',
`user_name` varchar(64) NOT NULL COMMENT '用户名称',
`user_password` varchar(32) NOT NULL COMMENT '用户密码',
`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
②角色表
CREATE TABLE `sys_role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) NOT NULL COMMENT '角色名称',
`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
③中间表
CREATE TABLE `sys_user_role` (
`role_id` bigint(32) NOT NULL COMMENT '角色id',
`user_id` bigint(32) NOT NULL COMMENT '用户id',
PRIMARY KEY (`role_id`,`user_id`),
KEY `FK_user_role_user_id` (`user_id`),
CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
9.2创建实体类
【用户类】
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state;
//设置多对多关系:表示一个用户选择多个角色
private Set<Role> roles = new HashSet<Role>();
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public Long getUser_id() {
return user_id;
}
public void setUser_id(Long user_id) {
this.user_id = user_id;
}
public String getUser_code() {
return user_code;
}
public void setUser_code(String user_code) {
this.user_code = user_code;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_password() {
return user_password;
}
public void setUser_password(String user_password) {
this.user_password = user_password;
}
public String getUser_state() {
return user_state;
}
public void setUser_state(String user_state) {
this.user_state = user_state;
}
}
【角色类】
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
//一个角色被多个用户选择:放置一个用户的集合
private Set<User> users = new HashSet<User>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public Long getRole_id() {
return role_id;
}
public void setRole_id(Long role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_memo() {
return role_memo;
}
public void setRole_memo(String role_memo) {
this.role_memo = role_memo;
}
}
9.3创建映射
【用户的映射文件配置】
<?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="com.itheima.hibernate.domain.User" table="sys_user">
<!-- 建立OID与主键的映射 -->
<id name="user_id" column="user_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与字段的映射 -->
<property name="user_code" column="user_code"/>
<property name="user_name" column="user_name"/>
<property name="user_password" column="user_password"/>
<property name="user_state" column="user_state"/>
<!-- 建立与角色的多对多的关系 -->
<!-- name对方集合的属性名称 //table 放的是中间表的名称 -->
<set name="roles" table="sys_user_role" cascade="sava-update">
<key column="user_id"/>
<!-- column 当前对象对应中间表的外键名称 -->
<many-to-many class="com.itheima.hibernate.domain.Role" column="role_id"/>
<!-- class对方类的全路径 column 对方对象在中间表的外键名称 -->
</set>
</class>
</hibernate-mapping>
【角色的映射配置】
<?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="com.itheima.hibernate.domain.Role" table="sys_role">
<!-- 建立OID与主键的映射 -->
<id name="role_id" column="role_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与字段的映射 -->
<property name="role_name" column="role_name"/>
<property name="role_memo" column="role_memo"/>
<!-- 建立与用户多对多的关系 -->
<set name="users" table="sys_user_role" inverse="true">
<key column="role_id"/>
<many-to-many class="com.itheima.hibernate.domain.User" column="user_id"/>
</set>
</class>
</hibernate-mapping>
十、hibernate的查询方式
10.1OID查询
OID检索:hibernate根据对象的OID (主键)进行检索
①get方法:
Customer customer = session.get(Customer.class,1l);
②load方法:
Customer customer = session.load(Customer.class,1l);
10.2对象导航检索
hibernate根据已经查询到的对象,获得其关联对象的一种查询方式。
LinkMan linkMan = session.get(LinkMan.class,1l);
Customer customer = linkMan.getCustomer();
或者
Customer customer = session.get(Customer.class,2l);
Set<LinkMan> linkMans = customer.getLinkMans();
10.3HQL检索
hibernate的查询语言,是一种面向对象的查询语言,语法类似sql,通过session.createQuery(),用于接受一个HQL进行查询
①简单查询
public void test6(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
②别名查询
public void test7(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Query query = session.createQuery("select c from Customer c");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
③hibernate的排序查询
public void test8(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
List<Customer> list = session.createQuery("from Customer order by cust_id desc").list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
④条件查询
public void test9(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Query query = session.createQuery("from Customer where cust_id = ?");
query.setParameter(0, 4l);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
public void test10(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Query query = session.createQuery("from Customer where cust_source = ? and cust_name like ?");
query.setParameter(0, "朋友推荐");
query.setParameter(1, "罗%");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
还有一个:
Query query = session.createQuery("from Customer where cust_source = :aaa and cust_name like :bbb");
query.setParameter("aaa", "朋友推荐");
query.setParameter("bbb", "罗%");
⑤投影查询:查询对象的某些或某个属性
List<Object> list = session.createQuery("select c.cust_name from Customer c").list();
List<Object[]> list = session.createQuery("select c.cust_name,c.cust_source from Customer c").list();
查询多个属性然后放入到一个对象中去:
List<Customer> list = session.createQuery("select new Customer(cust_name,cust_source) from Customer").list();
⑥分页查询
Query query2 = session.createQuery("from LinkMan");
query2.setFirstResult(10);
query2.setMaxResults(20);
⑦分组统计查询
聚合函数的使用:count(),max(),min(),avg(),sum()
List<Object[]> list = session.createQuery("select cust_source,count(*) from Customer group by cust_source").list();
⑧多表查询
SQL的多表查询
1连接查询
①交叉连接:笛卡尔积
select * from A,B;
②内连接:inner join (inner 可以省略)
隐式内连接:
select * from A,B where A.id = B.aid;
显式内连接:
select * from A inner join B on A.id = B.aid;
③外连接:
左外连接:left outer join(outer 可以省略)
select * from A left outer join B on A.id = B.aid;
右外连接:right out join (outer 可以省略)
select * from A right outer join B on A.id = B.aid;
2子查询
Hibernate的多表查询相对于SQL的多表查询大致差不多,但是其在内连接与外连接多了:迫切内连接和迫切左外连接
10.4QBC查询
query by criteria :一种更加面向对象的查询方式
①简单查询
List<Customer> list = session.createCriteria("Customer.class").list();
②排序查询
Criteria criteria = session.createCriteria("Customer.class");
criteria.addOrder(Order.desc("cust_id"));
desc:降序
asc:升序
③分页查询
Criteria criteria = session.createCriteria("Customer.class");
criteria.setFirstResult(10);
criteria.setMaxResults(20);
④条件查询
符号 | 含义 |
---|---|
eq | = |
gt | > |
ge | >= |
lt | < |
le | <= |
ne | <> |
还有:like、in、and、or
Criteria criteria = session.createCriteria("Customer.class");
criteria.add(Restrictions.like("cust_name", "罗%"));
criteria.add(Restrictions.eq("cust_source","小广告"));
10.5使用SQL查询
SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");