Hibernate从0开始,入门到放弃,一文上手

4 篇文章 0 订阅

背景

   
    公司的任务,二开,架构:Spring MVC Hibernate,前端用DHTMLX,MyEclipse开发,没用Maven构建,脑壳疼,因为,我对Hibernate不熟,而且想吐,因为DHTMLX开发貌似只有官方API靠谱,网上基本没资料。不过还是得做,所以特写此文,快速搞定这个Hibernate吧。看能不能在一两天之内搞明白。因为后续我要跟着二开,所以遵从公司环境:MyEclipse、SqlServer、手动jar包,如果连接MySQL啥的,导入相应驱动包,我这个SqlServer驱动包是sqljdbc4.jar
PS:本人会MyBatis,所以不会把这个Hibernate写的很多很深,能正常开发就ok
事实证明只用一天左右时间就差不多了
  

Hibernate介绍

    全自动ORM框架,刚开始知道这个就够了,好了,下班。
  

环境搭建

    1.准备jar,并导入
    jar包
    2.写实体类
    实体类
Hibernate可以根据表生成数据库表,所以建好库之后就不用建表去了
   
    3.配置实体类和数据库表的映射关系
   
        在实体类的包里创建xml配置文件,引入约束,并配置
   
结构
mapping
   
   4.创建Hibernate核心配置文件
   
      核心配置文件的名字和位置是固定的,位置在src下,名称是:hibernate.cfg.xml,引入约束,详细配置
   
   主要有三个部分:a>配置数据库信息、b>配置hibernate信息、c>配置映射文件信息
   
在这里插入图片描述

第一个程序

   先整理一下我们都做了什么:引入了所需jar包、创建了实体类及映射关系、创建了配置文件。那么,接下来的步骤也整理下:
   1.加载hibernate核心配置文件
   2.黄建SessionFactory对象
   3.使用SessionFactory创建Session
   4.开启事务
   5.写具体逻辑crud操作
   6.提交事务
   7.关闭资源

package cn.xx.test;

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

import cn.xx.entity.Shop;

/**
 * 
 * @author Administrator
 * 测试类
 */
public class Test {
	
	public static void main(String[] args) {
//		1.加载hibernate核心配置文件
		//到src下找到名称是hibernate.cfg.xml
		//在hibernate里封装对象
		Configuration cfg = new Configuration();
		cfg.configure();
		
//		2.创建SessionFactory对象
		//读取hibernate核心配置文件,创建SessionFactory
		//在过程中,根据映射关系,在配置数据库里把表创建
		SessionFactory sessionFactory = cfg.buildSessionFactory();
		
//		3.使用SessionFactory创建Session
		//类似于连接
		Session session = sessionFactory.openSession();
		
//		4.开启事务
		Transaction tx = session.beginTransaction();
		
//		5.写具体逻辑crud操作
		//添加
		Shop shop = new Shop();
		shop.setId(1);
		shop.setName("包子");
		shop.setEmail("zo10010@163");
		session.save(shop);
//		6.提交事务
		tx.commit();
//		7.关闭资源
		session.close();
		sessionFactory.close();
	}
}

然后,表也有了,数据也有了。

   

单例模式解决频繁创建、关闭SessionFactory

   ps:这里可忽略,重新写。我后来在核心配置文件里配置了本地session绑定,并且把session也搞到这个单例里了。求生欲:此单例不完善,具体最优单例请移驾百度,核心思想就是为了解决频繁创建等问题。

package cn.xx.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	private static final Configuration config;   
    private static final SessionFactory factory;   
    static {  // 静态初始化块,加载配置信息 
        config = new Configuration().configure();  // 读取文件 
        factory = config.buildSessionFactory();   
    }   
    public static SessionFactory getSessionFactory(){   
        return factory;   
           
    } 
}

继续CRUD:根据id查询

   

		Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction tx = session.beginTransaction();
		
		//根据id查询使用session.get(),第一个参数是实体类.class,第二个参数是id
		Shop shop = session.get(Shop.class, 1);
		System.out.println(shop);
		
		tx.commit();
		session.close();

继续CRUD:修改

		Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction tx = session.beginTransaction();
		
		
		//修改id为2的name值,先查,再改
		Shop shop = session.get(Shop.class, 2);
		shop.setName("烧烤");
		session.update(shop);
		
		tx.commit();
		session.close();

继续CRUD:删除

Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction tx = session.beginTransaction();
		
		
		//删除
		Shop shop = session.get(Shop.class, 2);
		session.delete(shop);
		
		tx.commit();
		session.close();

继续CRUD:saveOrupdate

//不设id,就是insert,对象与session没有关联,这是瞬时态
		Shop shop = new Shop();
		shop.setName("包子");
		shop.setEmail("zo10010");
		session.saveOrUpdate(shop);
Hibernate: 
    insert 
    into
        U_Shop
        (name, email) 
    values
        (?, ?)

//设了id就是修改,对象与session没有关联,这是托管态
		Shop shop = new Shop();
		shop.setId(3);
		shop.setName("辣条");
		shop.setEmail("zo10010");
		session.saveOrUpdate(shop);
Hibernate: 
    update
        U_Shop 
    set
        name=?,
        email=? 
    where
        id=?

//这也是update,对象与session有关联,这是持久态
		Shop shop = session.get(Shop.class, 3);
		shop.setName("啤酒");
		session.saveOrUpdate(shop);
Hibernate: 
    update
        U_Shop 
    set
        name=?,
        email=? 
    where
        id=?

Hibernate的缓存

      Hibernate有一级缓存和二级缓存
      一级缓存:使用范围在session,就是说从session打开到关闭,默认是打开的,存储数据必须是持久态。比如我去根据id查了一次数据库,在一个session里我又去根据这个id去查,那么第一次查的时候是查的数据库,并且在session里有缓存,第二次查的时候就不去数据库查了,而是在缓存里拿数据。
      二级缓存:目前不使用了,redis替代了它,所以不研究了

Hibernate的事务

      关于事务的特性,如果不考虑隔离性,会出现脏读、不可重复读、虚读问题,MySQL默认隔离级别是repeatable read,这里不做深究。这里重点搞一下关于事务的规范代码的写法
      基本结构:

		try {
			开启事务
			提交事务
		} catch () {
			回滚事务
		} finally {
			关闭
		}
		Session session = null;
		Transaction tx = null;
		try {
			session = HibernateUtil.getSessionFactory().openSession();
			tx = session.beginTransaction();
			//操作
			Shop shop = session.get(Shop.class, 3);
			shop.setName("啤酒");
			session.saveOrUpdate(shop);
			
		} catch (Exception e) {
			e.printStackTrace();
			tx.rollback();
		} finally {
			session.close();
		}

Hibernate的三个API

   Query对象:
      使用Query对象,不需要写SQL,但是要写HQL。
      SQL和HQL的区别:SQL是操作数据库的表、字段,HQL是操作实体类和属性

		//创建Query对象
		//方法里边写hql语句
		Query query = session.createQuery("from Shop");
		//调用query里的方法得到结果
		List<Shop> list = query.list();
		for (Shop shop : list) {
			System.out.println(shop);
		}

   Criteria对象:
      啥也不用写,直接调用里边的方法,没错,我也很震精,感兴趣完事去看源码

//创建Criteria对象
		//方法里边参数是实体类class
		Criteria createCriteria = session.createCriteria(Shop.class);
		List<Shop> list = createCriteria.list();
		for (Shop shop : list) {
			System.out.println(shop);
		}

   SQLQuery对象:
      这个是调用底层sql实现,返回的是个数组,数组,数组,这个看看就行了。。一切从简

//创建SQLQuery对象
		//参数是普通sql语句
		SQLQuery query = session.createSQLQuery("select * from U_Shop");
		List<Object[]> list = query.list();
		for (Object[] objects : list) {
			System.out.println(Arrays.toString(objects));
		}

关于表的关系

   都知道,表关系有:一对一、一对多、多对多。
      一对一:就像你跟你女盆友的关系
      一对多:一个公司有好多员工
      多对多:一个订单可以有好多商品,一个商品属于多个订单

一对多操作

      背景:一个公司有好多员工
   
      1.搞两个实体类:公司类、员工类

package cn.xx.entity;

import java.util.Set;
/**
 * 公司类
 * @author Administrator
 *
 */
public class Firm {
	
	private int id;
	private String name;
	private String addr;
	
	//hibernate规定一对多集合用set,因为set值不允许重复
	private Set<User> userSet;
	
	
	
	public Set<User> getUserSet() {
		return userSet;
	}
	public void setUserSet(Set<User> userSet) {
		this.userSet = userSet;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	
	
}

package cn.xx.entity;
/**
 * 员工类
 * @author Administrator
 *
 */
public class User {
	
	private int id;
	private String name;
	private int sex;
	
	//这个员工所属的公司
	private Firm firm;
	
	
	
	public Firm getFirm() {
		return firm;
	}
	public void setFirm(Firm firm) {
		this.firm = firm;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getSex() {
		return sex;
	}
	public void setSex(int sex) {
		this.sex = sex;
	}
	
}

      2.表的配置,主要是配置外键关系

<?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>
	<!-- name里写实体类的全路径,table写表名 -->
	<class name="cn.xx.entity.Firm" table="U_Firm">
		<!-- 配置实体类id与表id对应 
			name的值是实体类唯一id的值
			column为表的列名-->
		<id name="id" column="id">
			<!-- 设置数据库id增长策略 native -->
			<generator class="native"></generator>
		</id>
		<!-- 配置其他属性与表的对应 -->
		<property name="name" column="name"></property>
		<property name="addr" column="addr"></property>		
		
		<!-- 在公司映射文件中,表示所有员工
			使用set标签表示所有员工
			set标签里有name属性,属性值卸载客户实体类里面表示联系人的set集合名称
		 -->
		 <set name="userSet">
		 	<!-- 一对多建表
		 		hibernate机制是双向维护外键,双方都要配置外键
		 	 -->
		 	 <key column="fuid"></key>
		 	 <!-- 公司所有的员工,class里写员工实体类全路径 -->
		 	 <one-to-many class="cn.xx.entity.User"/>
		 </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>
	<!-- name里写实体类的全路径,table写表名 -->
	<class name="cn.xx.entity.User" table="U_User">
		<!-- 配置实体类id与表id对应 
			name的值是实体类唯一id的值
			column为表的列名-->
		<id name="id" column="id">
			<!-- 设置数据库id增长策略 native -->
			<generator class="native"></generator>
		</id>
		<!-- 配置其他属性与表的对应 -->
		<property name="name" column="name"></property>
		<property name="sex" column="sex"></property>		
		
		<!-- 表示员工所属公司
			name属性,因为在公司实体类使用Firm对象表示,写Firm名称
			class属性,Firm全路径
			column属性,外键名称
		 -->
		<many-to-one name="Firm" class="cn.xx.entity.Firm" column="fuid"></many-to-one>
	</class>
</hibernate-mapping>

      3.核心配置,主要是把映射文件配置到核心配置文件中

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<!-- 1.配置数据库信息 -->
		<property name="hibernate.connection.driver_class">
			com.microsoft.sqlserver.jdbc.SQLServerDriver
		</property>
		<property name="hibernate.connection.url">jdbc:sqlserver://127.0.0.1:1433;databaseName=TEST</property>
		<property name="hibernate.connection.username">sa</property>
		<property name="hibernate.connection.password">xx2255</property>
		
		<!-- 2.配置hibernate信息 -->
		<!-- 输出底层sql语句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- 输出底层sql语句格式 -->
		<property name="hibernate.format_sql">true</property>
		<!-- hibernate帮创建表,需要配置之后
			uptate:如果有表,更新,如果没有,创建 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 配置数据库方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
		<!-- 绑定本地session -->
		<property name="hibernate.current_session_context_class">thread</property>
		
		
		
		<!-- 3.把映射文件放到核心配置文件中 -->
		<mapping resource="cn/xx/entity/User.hbm.xml"/>
		<mapping resource="cn/xx/entity/Firm.hbm.xml"/>
		
	</session-factory>
	
</hibernate-configuration>

      4.弄个测试类运行一下子,然后去数据库看一下创建好的表和外键关系,ok
      5.测试类搞一下级联保存数据,哦对了,还有个配置别忘了,公司映射配置里加cascade=“save-update”,这个配置是做级联保存的操作。关于一对多级联操作就到这了,不搞级联删除了,一般业务里边也没这个功能,要不一个操作把数据都删了,多恐怖,不亚于删数据库。

		Firm f = new Firm();
		f.setName("bat");
		f.setAddr("地球");
		
		User u = new User();
		u.setName("break");
		u.setSex(1);
		
		Set<User> userSet = new HashSet<User>();
		userSet.add(u);
		f.setUserSet(userSet);
		
		//保存
		session.save(f);
			<set name="userSet" cascade="save-update">
		 	<!-- 一对多建表
		 		hibernate机制是双向维护外键,双方都要配置外键
		 	 -->
		 	 <key column="fuid"></key>
		 	 <!-- 公司所有的员工,class里写员工实体类全路径 -->
		 	 <one-to-many class="cn.xx.entity.User"/>
		 </set>

      6.关于员工跳槽,在做这儿的时候报了一个错:HHH000142: Javassist Enhancement failed: cn.xx.entity.Firm,这个错是因为一个关于懒加载机制,解决方案贴在代码下边,然后,由于是双向外键维护,性能又有损耗,所以,一般开发中不会把外键写死,而是做逻辑外键,这里呢就先这样吧

		//先查到你想跳的公司
		Firm f = session.get(Firm.class, 3);
		//再查你自己
		User u = session.get(User.class, 2);
		//把自己公司(bat)改成你想跳公司
		Set<User> userSet = new HashSet<User>();
		userSet.add(u);
		u.setFirm(f);
<many-to-one lazy="false" name="Firm" class="cn.xx.entity.Firm" column="fuid"></many-to-one>

多对多操作

      根据上边一对多的操作,确实不想写死主键,所以不去搞复杂的配置了。这里描述一下,我建了三个实体类搞多对多,一个user类、一个role类、一个userRole类,把粒度搞到最小。user里只有id和name,role里只有id和role,userRole里有id、userid、roleid,就建三个普通的表即可,具体要查询的话,肯定是靠逻辑外键做联合查询,这里不贴代码了,赶紧把查询搞起来

Hibernate查询方式

      Hibernate提供5种查询方式
         1.对象导航查询
         2.OID查询
         3.hql查询
         4.QBC查询
         5.本地sql查询
      搞到这儿,我小拇指头一哆嗦,我的发可儿?这是人干的事?一个查询都能整出来这么多花样?再不尽快搞定这Hibernate,我就要准备提桶跑路跳槽了。默默点了一根寂寞,隔着窗纱面对着黑暗,我知道,黑暗也在凝视着我,不过据我多年的算命经验,肯定有最优雅的一两种写法。
警告:要开始输出了,非战斗人员请撤离,别的都是扯淡,只有查询才是核心

      1.对象导航查询:
               像上边那个一对多,根据id查到了公司,又通过公司查到所有员工,这个过程就是对象导航查询
      2.OID查询:
               根据id查询某一条记录,返回对象,这叫OID查询
      3.hql查询:
               Query对象,写hql语句实现查询
      4.QBC查询:
               Criteria对象
      5.本地sql查询:
               SQLQuery对象,使用普通sql实现查询

      嗯?貌似咱上边都用过。一个个弄一弄再。

Hibernate的对象导航查询

      回顾一下上边一对多关系的那个业务,外键写死的那个。对象导航查询是基于配置了物理外键查询的,至于能不能用逻辑外键,我暂时还不清楚。那么,看下物理外键-对象导航查询:

Firm f = session.get(Firm.class,1);
//对象导航
f.getUserSet()

      意思就是说,因为有物理外键,我先查id为1的公司,得到了这个公司的所有信息,因为公司信息里边配置了UserSet集合,所以我们可以直接拿到这个集合,就得到了这个公司所有的员工信息。over。现在我的疑问越来越强烈,逻辑外键能不能实现对象导航?YY一下,首先我们不直接操作语句,Hibernate完全替我们做了,如果想不配置物理外键而是用逻辑外键,我们就得告诉Hibernate我们的逻辑外键的存在,这个该怎么操作呢?带着这个疑问继续往下搞,这里知道了对象导航查询的原理就可以了

Hibernate的OID查询

      不废话,直接贴代码,我感觉我被侮辱了,表示已当场去世。

Firm f = session.get(Firm.class,1);

      没错,这就是OID查询,作为一名老祖安,我已无力吐槽

Hibernate的HQL查询

      Hibernate Query Language,与SQL相似,上边貌似说过,SQL是操作数据库的表和字段,HQL是操作实体类和属性
      
      常用hql语句

HQL查询所有

      直接上代码,省略创建session,try等,跟上边一样,只贴try里的代码
      结构:from 实体类名

		//创建Query对象
		Query query = session.createQuery("from Role");
		//返回一个list
		List<Role> list = query.list();
		//遍历list
		for (Role role : list) {
			System.out.println(role);
		}

      输出:

Hibernate: 
    select
        role0_.id as id1_0_,
        role0_.role as role2_0_ 
    from
        U_Role role0_
1----老板
2----司机

HQL条件查询

      结构:from 实体类名 where 属性名 =? and 属性名 =?
                  from 实体类名 like ?

		Query query = session.createQuery("from User where sex = ? and name like ?");
		//设置条件值,
		//向?里设值,第一个参数是?的位置,第二个参数是值,位置是从0开始的
		query.setParameter(0, 1);
		query.setParameter(1, "b%");
		//得到结果
		List<User> list = query.list();
		for (User user : list) {
			System.out.println(user);
		}

      输出:

Hibernate: 
    select
        user0_.id as id1_1_,
        user0_.name as name2_1_,
        user0_.sex as sex3_1_ 
    from
        U_User user0_ 
    where
        user0_.sex=? 
        and (
            user0_.name like ?
        )
2----break----1

HQL排序

      结构:from 实体类名 order by 属性名 asc/desc

		Query query = session.createQuery("from Role order by id desc");
		List<Role> list = query.list();
		for (Role role : list) {
			System.out.println(role);
		}

      输出:

Hibernate: 
    select
        role0_.id as id1_0_,
        role0_.role as role2_0_ 
    from
        U_Role role0_ 
    order by
        role0_.id desc
2----司机
1----老板

HQL分页

		Query query = session.createQuery("from Role");
		//设置从哪开始
		query.setFirstResult(0);
		//设置每页几条数据
		query.setMaxResults(2);
		
		List<Role> list = query.list();
		for (Role role : list) {
			System.out.println(role);
		}

HQL投影查询

      其实就是查询部分字段
      结构:select 属性1,属性2 from 实体类名
      注意:select 后不能写*号

HQL聚集函数

      count、sum、svg、max、min
      count结构:select count(某属性) from 实体类名

		Query query = session.createQuery("select count(id) from Role");
		
		Object uniqueResult = query.uniqueResult();
		System.out.println(uniqueResult);

HQL多表查询

      首先,在sql里多表连查有内连、左外连、右外连,这里跟上边表、实体类没关系,字段全靠YY
      内连:select * from user u,role r where u.roleid = r.id
              select * from user u inner join role r on u.roleid = r.id
      左外连:select * from user u left join role r on u.roleid = r.id
      右外连:select * from user u right join role r on u.roleid = r.id
      
      那么在HQL里呢?
      HQL里有:内连、左外连、右外连、迫切内连、迫切左外连

      这部分我见都是基于物理外键,我发过誓不做物理外键,所以这部分内容如果公司需要,再去移驾百度去搞一搞。
      不过我做了我自己的逻辑外键

Query query = session.createQuery("select u.name from User u , Role r where u.roleid = r.id and u.id = 2 ");
		

      效果:

Hibernate: 
    select
        user0_.name as col_0_0_ 
    from
        U_User user0_ cross 
    join
        U_Role role1_ 
    where
        user0_.roleid=role1_.id 
        and user0_.id=2

Hibernate的QBC查询

      不写语句进行各种查询,用屁股想都知道不写语句就肯定必须遵循人家封装好的方法,给方法里设置各种值实现Hibernate读懂你要干啥,我吐了,刚学了写hql,又叫我不写hql????下边贴一个QBC查询所有,剩下的不搞了,hql已经是我最大的容忍了

QBC查所有

		Criteria criteria = session.createCriteria(Role.class);
		List list = criteria.list();
		for (Object object : list) {
			System.out.println(object);
		}
Hibernate: 
    select
        this_.id as id1_0_0_,
        this_.role as role2_0_0_ 
    from
        U_Role this_
1----老板
2----司机
3----经理
4----宦官
5----黄上
6----站长
7----程序狗

所以总的来说,还是hql语句顺手点,毕竟跟sql类似,暂时就搞到这里,耗了两天时间,至于性能优化之类的机制、配置,也不搞了,都Hibernate了,还性能。。完后在这方面有啥经验,再做补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值