目录
一、表关系转映射文件
1.一对多关系
1.1映射文件配置
在映射文件中一的一方使用< set>来对多的一方的引用。
<?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 package="com.zgf.entity" >
<class name="Customer" table="tb_cst_customer" >
<id name="cust_id" >
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name" ></property>
...
<!-- 配置一对多关系 -->
<!--
name:多的一方集合的属性名称
即Customer类中的集合的属性名
private Set<LinkMan> linkMens= new HashSet<LinkMan>();
-->
<set name="linkMens">
<!--
key标签
column:多的一方外键的名称
-->
<key column="lkm_cust_id"></key>
<!--
one-to-many
class:多的一方类的全路径名
由于上面配置<hibernate-mapping package="com.zgf.entity" >
SaleVisit类属于此路径下的类可以直接写类名
-->
<one-to-many class="LinkMan"/>
</set>
</class>
</hibernate-mapping>
在多的一方映射文件中使用< many-to-one>实现对一的一方引用。
<?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 package="com.zgf.entity" >
<class name="LinkMan" table="tb_cst_linkman" >
<id name="lkm_id" >
<generator class="uuid"></generator>
</id>
......
<!-- 多对一 -->
<!--
many-to-one
name:SaleVisit类中引用一的一方对象的属性名
private Customer customer;
column:表中外键的名称
class:引用一的一方类的全路径
由于配置<hibernate-mapping package="com.zgf.entity" >
且SaleVisit与Customer在同一包下,只需要写类名即可
-->
<many-to-one name="customer" column="lkm_cust_id" class="Customer" />
</class>
</hibernate-mapping>
1.2级联操作
当以Customer对象为主控方,级联操作Customer对象以及LinkMan对象时,需要在Customer对应的映射文件< set>上配置cascade属性值,这样可以减少代码量。简化操作一定要使用save-update,不建议使用delete。
<!--
cascade:可取值save-update(级联保存或修改)、delete(级联删除)、all(delete,save-update)
-->
<set name="linkMens" cascade="save-update">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
1.3关系维护
在保存时,两方都会维护外键关系,关系维护两次,一次时在保存LinkMan时,在insert语句中维护一次,一次时保存Customer时,执行update更新LinkMan中外键一次,冗余了. 多余的维护关系语句,显然是客户这一端在维护关系。所以,我们要让一的一方放弃外键维护,由多的一方维护外键关系。在Customer对应的映射文件< set>中配置inverse属性。
<!--
inverse属性:配置关系是否维护
false(默认值):Customer维护关系
true:Customer放弃维护,全交给LinkMan维护
-->
<set name="saleVisit" cascade="save-update" inverse="true">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
测试代码
public void name() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c1 = new Customer();
c1.setCust_name("xx学院");
LinkMan m1 = new LinkMan();
m1.setLkm_name("吴老师");
m1.setCustomer(c1);
LinkMan m2 = new LinkMan();
m2.setLkm_name("赵老师");
m2.setCustomer(c1);
c1.getLinkMens().add(m1);
c1.getLinkMens().add(m2);
session.save(c1);
tx.commit();
session.close();
}
加入inverse属性之前生成的sql语句。
加入inverse之后生成的sql语句。减少了两次发送的update语句。
2.多对多关系
映射文件配置
此处以用户和角色为例,一个用户可以有多个角色,一个角色可以属于多个用户,这是典型的多对多关系,我们在用户映射文件中配置如下代码。
<!--
set标签
name:关联的另一方属性名称
private Set<Role> roles = new HashSet<Role>();
table:中间表的表名
-->
<set name="roles" table="sys_user_role" cascade="save-update" >
<!--
key标签
column:当前表中外键的名称
-->
<key column="user_id" ></key>
<!--
many-to-many
class:关联另一方类的全路径类名
column:关联另一方表在中间表中的外键
-->
<many-to-many class="Role" column="role_id" ></many-to-many>
</set>
在Role对应的配置文件中添加如下代码。
<!--
set标签
name:关联的另一方属性名称
private Set<User> users = new HashSet<User>();
table:中间表的表名
-->
<set name="users" table="sys_user_role" inverse="true" >
<!--
key标签
column:当前表中外键的名称
-->
<key column="role_id" ></key>
<!--
many-to-many
class:关联另一方类的全路径类名
column:关联另一方表在中间表中的外键
-->
<many-to-many class="User" column="user_id" ></many-to-many>
</set>
中间表
在多对多的保存操作中,如果进行了双向维护关系,就必须由一方放弃外键维护权,一般由被动方放弃,用户主动选择角色,角色就被动方,所以一般角色要放弃外键维护全。但是若只进行单向维护关系,那么就不要放弃外键维护权了。
二、Hibernate中的查询
1.OID检索方式
Customer customer = session.get(Customer.class, 1l);
Customer customer = session.load(Customer.class, 1l);
2.对象图导航检索
LinkMan linkMan = session.get(LinkMan.class,1l);
Customer customer = linkMan.getCustomer();
3.HQL检索
//基本查询
Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
//排序查询
Query query = session.createQuery("from Customer order by cust_id desc");
List<Customer> list = query.list();
//条件查询
//按位置绑定参数
Query query = session.createQuery("from Customer where cust_name=?");
query.setParameter(0, "zhangsan");
Customer customer = (Customer) query.uniqueResult();
//按名称绑定参数
Query query = session.createQuery("from Customer where cust_name=:name");
query.setParameter("name", "zhangsan");
Customer customer = (Customer) query.uniqueResult();
//分页查询
Query query = session.createQuery("from Customer");
query.setFirstResult(5);
query.setMaxResults(10);
List<Customer> list = query.list();
//统计查询
Query query = session.createQuery("select count(*) from Customer");
Long count = (Long) query.uniqueResult();
//投影查询
//投影查询一列
Query query = session.createQuery("select cust_name from Customer");
List<String> list = query.list();
//投影查询多列
Query query = session.createQuery("select cust_id,cust_name from Customer");
List<Object[]> list = query.list();
//投影构造方式查询
Query query = session.createQuery("select new Customer(cust_id,cust_name) from Customer");
List<Customer> list = query.list();
4.QBC检索
//简单查询
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
//条件查询
Criteria criteria = session.createCriteria(Customer.class);
//criteria.add(Restrictions.eq("cust_name", "zhangsan"));
criteria.add(Restrictions.like("cust_name","%zhang%"));
List<Customer> list = criteria.list();
//分页查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFirstResult(5);
criteria.setMaxResults(10);
List<Customer> list = criteria.list();
//排序查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(Order.desc("cust_id"));
List<Customer> list = criteria.list();
//排序查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setProjection(Projections.rowCount());
Long count = (Long) criteria.uniqueResult();
//离线查询
//可将条件封装,从web层传递到service在传递到dao层
DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
dc.add(Restrictions.gt("cust_id",60L));//大于等于
Criteria criteria = dc.getExecutableCriteria(session);
List<Customer> list = criteria.list();
5.本地SQL检索
//离线查询
SQLQuery query = session.createSQLQuery("select cust_id,cust_name from tb_cst_customer");
query.addEntity(Customer.class);
List<Customer> list = query.list();
6.多表查询
//HQL多表查询
//内连接
List<Object[]> list = session.createQuery("from Customer c inner join c.linkMans").list();
//迫切内连接
List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMens").list();
下图为普通内连接封装的list集合,很明显封装的结果为List<Object[]>,每一条数据都是封装两个对象的集合,从下图中可以看出集合中一个对象为Customer,customer对象中封装了一个LinkMan的set集合,集合中的另一个对象为LinkMan(linkMan被封装了两次)。
下图为迫切内连接封装的list集合,HQL语句中加入fetch后,会将属于客户的数据封装到Customer中,将数据联系人的数据封装到customer对象中的Set< LinkMan>集合中。迫切内连接查询结果会有重复数据,所以常加上关键字distinct。
三、查询优化
1.延迟加载
- 类级延迟加载:是指查询某个对象时,是否采用有延迟,通常在< class>标签上配置lazy属性。
- 关联级别延迟加载:是指查询一个对象关联的对象时,是否采用延迟加载,通常在< set>、< many-to-one>上配置lazy属性。
hibernate类级延迟加载默认时延迟的,使用load方法可以看,在执行该方法时并不会发送sql语句,当使用到该方法加载的对象时才会执行查询获得真实对象。
Customer customer = session.load(Customer.class,1l);
关联级别延迟加载,< set>标签上通常有三种lazy属性取值。
-
true:默认值,采用延迟加载。
-
false:检索关联对象时,不采用延迟加载。
-
extra:及其懒惰。
< many-to-one>标签上通常也是有三种取值。 -
proxy:默认值,取决于一的一方类上的lazy属性值。
-
false:检索关联对象时,不采用延迟加载。
-
no-proxy:不研究。
2.抓取策略
抓取策略指的是查询到某个对象的时候,如何抓取其关联的对象。在关联对象的标签上配置fetch属性。
< set>标签上通常有三个取值:
-
select:默认值,发送的是普通select语句查询。
-
join:发送的是一条迫切左外连接连接去查询。
-
subselect:发送的是一条子查询语句查询其关联的对象。
< many-to-one>标签上的fetch有两个取值: -
select:默认值,发送一条普通的select语句查询。
-
join:发送的是一条迫切左外连接连接去查询。
lazy与fetch配置使用可以很好的提高性能,fetch若设为join,那么lazy就会失效。
在< set> 上fetch和lazy的组合效果。
//集合级别的关联
//fetch:select 单表查询
//lazy:true 使用时才加载集合数据.
@Test
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.get(Customer.class, 2l);//发送一条select查询Customer
Set<LinkMan> linkMens = c.getLinkMens();//关联级别
System.out.println(linkMens);//发送一条select查询LinkMan
//----------------------------------------------------
tx.commit();
session.close();
}
//集合级别的关联
//fetch:select 单表查询
//lazy:false 立即记载集合数据
@Test
public void fun2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.get(Customer.class, 2l);//发送两条条select,一条查询Customer,一条查询LinkMan
Set<LinkMan> linkMens = c.getLinkMens();//关联级别
System.out.println(linkMens);
//----------------------------------------------------
tx.commit();
session.close();
}
//集合级别的关联
//fetch:select 单表查询
//lazy:extra 极其懒惰.与懒加载效果基本一致. 如果只获得集合的size.只查询集合的size(count语句)
@Test
public void fun3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.get(Customer.class, 2l);//发送一条select查询Customer
Set<LinkMan> linkMens = c.getLinkMens();//关联级别
System.out.println(linkMens.size());//发送一条select count(*) 统计语句
System.out.println(linkMens); //发送一条select查询LinkMan
//----------------------------------------------------
tx.commit();
session.close();
}
//集合级别的关联
//fetch:join 多表查询
//lazy:true|false|extra 失效.立即加载.
@Test
public void fun4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
Customer c = session.get(Customer.class, 2l); //发送一条迫切外连接直接将关联对象一起查询
Set<LinkMan> linkMens = c.getLinkMens();//关联级别
System.out.println(linkMens.size());
System.out.println(linkMens);
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch: subselect 子查询
//lazy: true 懒加载
public void fun5(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
String hql = "from Customer";
//子查询不能查询某一个对象,查询多个对象来测试
Query query = session.createQuery(hql);
List<Customer> list = query.list(); //发送一条select查询Customer
for(Customer c:list){
System.out.println(c);
System.out.println(c.getLinkMens().size());//发送一条select查询客户关联的LinkMan
System.out.println(c.getLinkMens());
}
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch: subselect 子查询
//lazy: false 立即加载
public void fun6(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
String hql = "from Customer";
Query query = session.createQuery(hql);
List<Customer> list = query.list();//发送子查询查询Custome及其关联的LinkMan
for(Customer c:list){
System.out.println(c);
System.out.println(c.getLinkMens().size());
System.out.println(c.getLinkMens());
}
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch: subselect 子查询
//lazy: extra 极其懒惰
public void fun7(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
String hql = "from Customer";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
for(Customer c:list){
System.out.println(c);
System.out.println(c.getLinkMens().size());
System.out.println(c.getLinkMens());
}
//----------------------------------------------------
tx.commit();
session.close();
}
在< many-to-one>上的组合效果
@Test
//fetch:select 单表查询
//lazy:proxy
//customer-true 懒加载
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);//发送一条select查询LinkMan
Customer customer = lm.getCustomer();
System.out.println(customer);//发送一条select查询Customer
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch:join 多表
//lazy: 失效
public void fun3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);//发送一条迫切外连接直接将关联对象一起查询
Customer customer = lm.getCustomer();
System.out.println(customer);
//----------------------------------------------------
tx.commit();
session.close();
}
@Test
//fetch:select 单表查询
//lazy:proxy
//customer-false 立即加载
public void fun2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//----------------------------------------------------
LinkMan lm = session.get(LinkMan.class, 3l);//发送两条条select,一条查询LinkMan,一条查询Customer
Customer customer = lm.getCustomer();
System.out.println(customer);
//----------------------------------------------------
tx.commit();
session.close();
}
3.批量抓取
批量抓取是指一次抓取关联对象的个数,通常在< set>上配置batch-size属性,如:batch-size=“4”,那么就一次抓取该对象的四个关联对象。还可以在< class>上配置。