HIbernate

①Hibernate是什么

Hibernate就是一个持久层的ORM框架,对JDBC进行了轻量级的封装。

ORM : Object Relational Mapping 对象关系映射,描述对象和数据库之间映射的元数据,自动把Java引用程序中的对象,持久化到关系型数据库的表中。通过操作Java对象可以,可以实现对数据库表的操作。可以理解为中间的一个纽带

②Hibernate环境的搭建

Hibernate快速入门

     下载:https://sourceforge.net/projects/hibernate/files/latest/download

      目录结构:

            documentation:Hibernate相关文档

            lib:Hibernate编译运行所需要的jar包,required下包含的是运行Hibernate5所必须的jar包

            project:存放Hibernate各种相关的源代码

     创建数据库表

     导入jar包:mysql+required文件夹下的jar包。  1+9

     创建实体:这里指类的对象能够持久化到数据库中,Hibernate使用的普通Java对象。

     创建映射文件:数据库→       ←对象  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">
   <!-- 配置表与实体对象的关系 -->
   <!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. -->
<hibernate-mapping package="cn.pb.domain" >
	<!-- 
		class元素: 配置实体与表的对应关系的
			name: 完整类名
			table:数据库表名
	 -->
	<class name="Customer" table="cst_customer" >
		<!-- id元素:配置主键映射的属性
				name: 填写主键对应属性名
				column(可选): 填写表中的主键列名.默认值:列名会默认使用属性名
				type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
						每个类型有三种填法: java类型|hibernate类型|数据库类型
				not-null(可选):配置该属性(列)是否不能为空. 默认值:false
				length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
		 -->
		<id name="cust_id"  >
			<!-- generator:主键生成策略(明天讲) -->
			<generator class="native"></generator>
		</id>
		<!-- property元素:除id之外的普通属性映射
				name: 填写属性名
				column(可选): 填写列名
				type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
						每个类型有三种填法: java类型|hibernate类型|数据库类型
				not-null(可选):配置该属性(列)是否不能为空. 默认值:false
				length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
		 -->
		<property name="cust_name" column="cust_name" >
			<!--  <column name="cust_name" sql-type="varchar" ></column> -->
		</property>
		<property name="cust_source" column="cust_source" ></property>
		<property name="cust_industry" column="cust_industry" ></property>
		<property name="cust_level" column="cust_level" ></property>
		<property name="cust_linkman" column="cust_linkman" ></property>
		<property name="cust_phone" column="cust_phone" ></property>
		<property name="cust_mobile" column="cust_mobile" ></property>
	</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>
	
		<!-- 
		#hibernate.dialect org.hibernate.dialect.MySQLDialect
		#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
		#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
		#hibernate.connection.driver_class com.mysql.jdbc.Driver
		#hibernate.connection.url jdbc:mysql:///test
		#hibernate.connection.username gavin
		#hibernate.connection.password
		 -->
		 <!-- 数据库驱动 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		 <!-- 数据库url -->
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate_32</property>
		 <!-- 数据库连接用户名 -->
		<property name="hibernate.connection.username">root</property>
		 <!-- 数据库连接密码 -->
		<property name="hibernate.connection.password">1234</property>
		<!-- 数据库方言
			不同的数据库中,sql语法略有区别. 指定方言可以让hibernate框架在生成sql语句时.针对数据库的方言生成.
			sql99标准: DDL 定义语言  库表的增删改查
					  DCL 控制语言  事务 权限
					  DML 操纵语言  增删改查
			注意: MYSQL在选择方言时,请选择最短的方言.
		 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		
		
		<!-- #hibernate.show_sql true 
			 #hibernate.format_sql true
		-->
		<!-- 将hibernate生成的sql语句打印到控制台 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 将hibernate生成的sql语句格式化(语法缩进) -->
		<property name="hibernate.format_sql">true</property>
		<!-- 
		## auto schema export  自动导出表结构. 自动建表
		#hibernate.hbm2ddl.auto create		自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
		#hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
		#hibernate.hbm2ddl.auto update(推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
		#hibernate.hbm2ddl.auto validate	校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
		 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 引入orm元数据
			路径书写: 填写src下的路径
		 -->
		<mapping resource="cn/pb/domain/Customer.hbm.xml" />
		
	</session-factory>
</hibernate-configuration>

     编写测试类:

package cn.pb.a_hello;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.pb.domain.Customer;

//测试Hibernate框架
public class Demo {

	@Test
	//保存客户
	public void fun1(){
        //加载配置文件中的信息,默认是src下的hibernate.cfg.xml,当然也可以有带参构造,完成指定路径的初始化
		Configuration conf = new Configuration().configure();
		//SessionFactory 相当于一个缓冲区,负责初始化,建立session,他是线程安全的,内部还维护了一个连接池
		SessionFactory sessionFactory = conf.buildSessionFactory();
		//session是应用程序和数据库交互的一个单线程对象,获取方法除了↓getCurrentSession()获得的session会与线程绑定,他是线程不安全的
		Session session = sessionFactory.openSession();
	    //Transaction 主要用来管理事务  commit  rollback
		Transaction tx = session.beginTransaction();
		
		Customer c = new Customer();
		c.setCust_name("google公司");
		
		session.save(c);//执行保存
		
		tx.commit();
		session.close();
		sessionFactory.close();
	}
}

③Hibernate中的实体规则

     持久化类:Hibernate是持久层的 ORM映射框架,专注于数据持久化的工作,所谓的持久化就是将没存中数据永久的存储在关系型数据库中

    持久化类的编写原则

            ①必须提供无参构造

            ②属性私有,对私有的属性提供共有的get和set方法

            ③持久化类的属性要尽量使用包装类类型

            ④持久化类要有一个唯一表示OID与表的主键对应

            ⑤持久化类尽量不要使用final修饰:Hiernate有延迟加载机制,在这个机制中会产生代理对象,Hibernate中的代理对象是使用字节码增强技术来实现,其中的方法是需要具体实现的。如果使用了final修饰持久化类,那么就不能产生子类。

    主键生成策略:

           自然主键:将具有业务含义的字段作为主键

           代理主键:把不具有业务含义的字段所谓主键

         1)increment:用于long、short、int类型,由Hibernate自动以递增的方式生成唯一标识,每次增量为1。只有当没有其它进程向同一张表中插入数据时才可以使用,不能再集群环境下使用。适用于代理主键

         2)identity:采用底层数据库本身提供的主键成成标识符,前提是数据库支持自动增长的数据类型,适用于代理主键

         3)sequence:Hibernate根据底层数据库序列生成标识符。前提数据库支持序列,如Oracle,适用于代理主键

         4)native:根据底层数据库对自动生成标识符能力来选择identity、sequence、hilo三种生成方式中的一种,适合跨数据平台开发,适用于代理主键

         5)uuid:Hibernate使用128位的UUID算法生成标识符。概算可以在网络环境下生成一个32位十六进制的字符串唯一的字符串标识,但因为字符串比整数类型占用太多的数据库空间,适用于代理主键

         6)由Java程序来生成标识符,不指定id元素的generator属性,则默认使用该主键生成策略。适用于自然主键。

④Hibernate中的对象状态

    Hibernate持久化对象的三种状态

         1)瞬时态(transient):

              也被称为临时态或者自由态→使用new创建,开辟内存空间。但是不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中没有记录,失去引用后被JVM回收。 信息携带的载体

         2)持久态(persistent):

               持久态的对象存在持久化标识OID,加入到了session缓存中,并且相关联的session没有关闭,在数据库中没有对应的记录。持久态对象是事务还未提交前变为持久态的。

         3)托管态(detached)

               也称为离线态或游离态。当某个持久化状态的实例与session的关联被关闭就变成了托管态。托管态存在持久化标识OID,并且任与数据库中的数据存在关联,只是失去当前的session关联。托管状态发生改变时,Hibernate不能检测到

 

⑤Hibernate的一级缓存

缓存:提高效率.hibernate中的一级缓存也是为了提高操作数据库的效率.

提高效率手段1:提高查询效率

提高效率手段2:减少不必要的修改语句发送

⑥Hibernate中的事务

事务的特性:a(原子性) c(一致性) i(隔离性) d(持久性)

事务的并发问题: 1、脏读    2、不可重复读(update)    3、幻读/虚读(insert)

事务的隔离级别:读未提交、读已提交(Oracle默认)、可重复读(MySQL默认)、串行化

如何在hibernate中指定数据库的隔离级别

在项目中如何管理事务

1)业务开始之前打开事务,业务执行之后提交事务. 执行过程中出现异常.回滚事务.

2)在dao层操作数据库需要用到session对象.在service控制事务也是使用session对象完成. 我们要确保dao层和service层使用的使用同一个session对象,用sf.getCurrentSession()方法即可获得与当前线程绑定的session对象

注:调用getCurrentSession方法必须配合主配置中的一段配置。其实就是将session和线程进行了绑定

⑦Hibernate中的批量查询

1)HQL查询-hibernate Query Language(不复杂时使用)

基本查询

条件查询

        ?号占位符

        命名占位符

分页查询

2)Criteria查询(单表条件查询):Hibernate自创的无语句面向对象查询

基本查询

条件查询

分页查询

查询总记录数

3)原生SQL查询(复杂的业务查询)

基本查询

     返回数组List

     返回对象List

条件查询

分页查询

自己补充的一个查询方法

hibernate命名查询的配置

①在对应的实体如user.hbm.xml中进行配置

       *class标签外,</hibernate-mapping>标签内

    <query name="user.editPassword">
    	UPDATE TUser SET password=? WHERE id=?
    </query>

②方法设置

	public void executeUpdate(String queryName,Object... objects){
		Session session = this.getSessionFactory().openSession();
		Query query = session.getNamedQuery("user.editPassword");
		int i=0;
		for (Object object : objects) {
			query.setParameter(i++, object);
		}
		query.executeUpdate();
	}

③方法调用

userDao.executeUpdate("user.editPassword",password,id);

⑧Hibernate中的关联关系

1)一对多 | 多对一

关系表达

       表中的表达:通过外键的形式来表达

       实体中的表达

       orm元数据中的表达

操作

	@Test
	//保存客户 以及客户 下的联系人
	public void fun1(){
		//1 获得session
		Session session = HibernateUtils.openSession();
		//2 开启事务
		Transaction tx = session.beginTransaction();
		//3操作
		Customer c = new Customer();
		c.setCust_name("刘强东");
		
		LinkMan lm1 = new LinkMan();
		lm1.setLkm_name("张三");
		
		LinkMan lm2 = new LinkMan();
		lm2.setLkm_name("李四");
		
		//表达一对多,客户下有多个联系人
		c.getLinkMens().add(lm1);
		c.getLinkMens().add(lm2);
		
		//表达对对对,联系人属于哪个客户
		lm1.setCustomer(c);
		lm2.setCustomer(c);
		
		
		session.save(c);
		session.save(lm1);
		session.save(lm2);
		
		//4提交事务
		tx.commit();
		//5关闭资源
		session.close();
	}

级联操作:最好用save-update,不建议使用delete.

关系维护:在保存时.两方都会维护外键关系.关系维护两次,冗余了. 多余的维护关系语句,显然是客户这一端在维护关系

2)多对多

关系表达

       表中的表达:通过第三张表的外键引用另外两张表的主键

       对象中的表达:

       orm源数据的表达

操作

	@Test
	//保存员工以及角色
	public void fun1(){
		//1 获得session
		Session session = HibernateUtils.openSession();
		//2 开启事务
		Transaction tx = session.beginTransaction();
		//-------------------------------------------------
		//3操作
		//1> 创建两个 User
		User u1 = new User();
		u1.setUser_name("张三");
		
		User u2 = new User();
		u2.setUser_name("李四");
		
		//2> 创建两个 Role
		Role r1 = new Role();
		r1.setRole_name("保洁");
		
		Role r2 = new Role();
		r2.setRole_name("保安");
		//3> 用户表达关系
		u1.getRoles().add(r1);
		u1.getRoles().add(r2);
		
		u2.getRoles().add(r1);
		u2.getRoles().add(r2);
		
		//4> 角色表达关系
		r1.getUsers().add(u1);
		r1.getUsers().add(u2);
		
		r2.getUsers().add(u1);
		r2.getUsers().add(u2);
		
		//5> 调用Save方法一次保存
		session.save(u1);
		session.save(u2);
		session.save(r1);
		session.save(r2);
		//-------------------------------------------------
		//4提交事务
		tx.commit();
		//5关闭资源
		session.close();
	}

级联操作

关系维护

或者以面向对象进行属性关系设置的时候,减少一方的设置

⑨查询补充

HQL语法

1)基础语法

		String hql = " from  cn.pb.Customer ";//完整写法
		String hql2 = " from  Customer "; //简单写法
		String hql3 = " from java.lang.Object "; 

2)排序

		String hql1 = " from  com.pb.Customer order by cust_id asc ";//完整写法
		String hql2 = " from  com.pb.Customer order by cust_id desc ";//完整写法

3)条件

		String hql1 = " from  cn.pb.domain.Customer where cust_id =? ";//完整写法
		String hql2 = " from  cn.pb.domain.Customer where cust_id = :id ";//完整写法
		Query query = session.createQuery(hql2);
		query.setParameter("id", 2l);

4)分页

		String hql1 = " from  cn.pb.domain.Customer  ";//完整写法	
		Query query = session.createQuery(hql1);
		// (当前页数-1)*每页条数
		query.setFirstResult(2);
		query.setMaxResults(2);

5)聚合

		String hql1 = " select count(*) from  cn.pb.domain.Customer  ";//完整写法
		String hql2 = " select sum(cust_id) from  cn.pb.domain.Customer  ";//完整写法
		String hql3 = " select avg(cust_id) from  cn.pb.domain.Customer  ";//完整写法
		String hql4 = " select max(cust_id) from  cn.pb.domain.Customer  ";//完整写法
		String hql5 = " select min(cust_id) from  cn.pb.domain.Customer  ";//完整写法
		Query query = session.createQuery(hql5);		
		Number number  = (Number) query.uniqueResult();

6)投影

		String hql1 = " select cust_name from  cn.pb.domain.Customer  ";
		String hql2 = " select cust_name,cust_id from  cn.pb.domain.Customer  ";
		String hql3 = " select new Customer(cust_id,cust_name) from  cn.pb.domain.Customer  ";

7)多表查询

	    //HQL 内连接 => 将连接的两端对象分别返回.放到数组中.
	    String hql = " from Customer c inner join c.linkMens ";
		Query query = session.createQuery(hql);
		List<Object[]> list = query.list();

    	//HQL 迫切内连接 => 帮我们进行封装.返回值就是一个对象
		String hql = " from Customer c inner join fetch c.linkMens ";
		Query query = session.createQuery(hql);
		List<Customer> list = query.list();

	    //HQL 左外连接 => 将连接的两端对象分别返回.放到数组中.
		String hql = " from Customer c left join c.linkMens ";
		Query query = session.createQuery(hql);
		List<Object[]> list = query.list();

	    //HQL 右外连接 => 将连接的两端对象分别返回.放到数组中
		String hql = " from Customer c right join c.linkMens ";

Criteria语法

1)基本

		Criteria c = session.createCriteria(Customer.class);
		List<Customer> list = c.list();

2)条件

		Criteria c = session.createCriteria(Customer.class);
//		c.add(Restrictions.idEq(2l));
		c.add(Restrictions.eq("cust_id",2l));

3)分页

		Criteria c = session.createCriteria(Customer.class);
		//limit ?,? 
		c.setFirstResult(0);
		c.setMaxResults(2);

4)排序

		Criteria c = session.createCriteria(Customer.class);
		c.addOrder(Order.asc("cust_id"));
		//c.addOrder(Order.desc("cust_id"));

5)统计

		Criteria c = session.createCriteria(Customer.class);
		//设置查询目标
		c.setProjection(Projections.rowCount());

离线查询

	@Test
	public void fun1(){
		//Service/web层
		DetachedCriteria dc  = DetachedCriteria.forClass(Customer.class);
		dc.add(Restrictions.idEq(6l));//拼装条件(全部与普通Criteria一致)
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		Criteria c = dc.getExecutableCriteria(session);
		List list = c.list();
		System.out.println(list);
		tx.commit();
		session.close();
	}

查询优化

抓取策略:应用程序需要在关联关系之间进行导航的时候,hibernate如何获取对象的策略,适当的选择抓取策略可提高性能

懒加载:关联关系对象的默认加载方式

类级别延迟加载 (class)

get方法:没有任何策略.调用即立即查询数据库加载数据.

load方法: 应用类级别的加载策略

关联级别延迟加载

注:如果fetch设置为join,lazy就失效了。

结论:为了提高效率.fetch的选择上应选择select. lazy的取值应选择 true. 全部使用默认值.

session失效问题:问题的引入是由于fetch和lazy默认属性致使查询性能低下,在必要时可以通过servlet的session来获取查询结果

批量抓取:减少查询语句,每次将一定数量的结果放入缓存中

一个客户有两个联系人,不加下面这句,查询的时候出现三条查询语句。加了之后之后出现两条查询语句,联系人直接使用limit查询出来了

补充:Hibernate离线查询

场景:区域(进行划分形成了分区)   分区   定区(中间表定义分区和取派员之间的联系)

           现在要对分区进行条件查询。条件有省市区,这些数据在区域中的,这里要进行类似别名的离线查询。

web层的封装

	public String pageQuery() throws IOException{
		pageBean.setPage(page);
		pageBean.setPageSize(rows);
		String addresskey = model.getAddresskey();
		if(StringUtils.isNotBlank(addresskey)){
			detachedCriteria.add(Restrictions.like("addresskey", "%"+addresskey+"%"));
		}
		Region region = model.getRegion();
		if(region!=null){
			String province = region.getProvince();
			String city = region.getCity();
			String district = region.getDistrict();
			detachedCriteria.createAlias("region", "r");
			if(StringUtils.isNotBlank(province)){
				detachedCriteria.add(Restrictions.like("r.province", "%"+province+"%"));
			}
			if(StringUtils.isNotBlank(city)){
				detachedCriteria.add(Restrictions.like("r.city", "%"+city+"%"));
			}
			if(StringUtils.isNotBlank(district)){
				detachedCriteria.add(Restrictions.like("r.district", "%"+district+"%"));
			}
		}
		subareaService.pageQuery(pageBean);
		ServletActionContext.getResponse().setContentType("text/html;charset=utf-8");
		ServletActionContext.getResponse().getWriter().print(this.objectToJson(pageBean, excludesItem));
		return NONE;
	}

dao层的改变

	@Override
	public void findByPageBean(PageBean pageBean) {
		Integer currentPage = pageBean.getPage();
		Integer pageSize = pageBean.getPageSize();
		DetachedCriteria criteria = pageBean.getDetachedCriteria();
		criteria.setProjection(Projections.rowCount());
		List<Long> countList = (List<Long>) this.getHibernateTemplate().findByCriteria(criteria);
		pageBean.setTotal(countList.get(0));
		criteria.setProjection(null);
		criteria.setResultTransformer(DetachedCriteria.ROOT_ENTITY);
		List<Staff> rows =  (List<Staff>) this.getHibernateTemplate().findByCriteria(criteria,(currentPage-1)*pageSize, pageSize);
		pageBean.setRows(rows);
		
	}

这样就可以实现关联条件查询。

补充:关联查询补充:根据外键查,criteria的语法

		DetachedCriteria criteria = DetachedCriteria.forClass(Subarea.class);
		criteria.add(Restrictions.eq("decidedzone.id", decidedzoneid));

补充:hql实现通过用户来查询权限信息

select distinct f from Function f left outer join f.roles r 
                                  left outer join r.users u where u.id=?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值