hibernate基础
在学习ORM框架之前,我们都是使用jdbc对数据库进行操作,使用Statement、PreparedStatement进行操作。然后封装了DbUtil操作数据库。
1.搭建开发环境
1.1 jar包 【5.1.16】
https://sourceforge.net/projects/hibernate/
>equired目录下的
>数据库驱动
>c3p0
2.2 创建POJO
必须实现Serializable接口

package cn.getword.domain; import java.io.Serializable; /** * 必须可序列号 db_hibernate */ public class User implements Serializable { private Integer id; private String username; private Integer age; private Integer deleted; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getDeleted() { return deleted; } public void setDeleted(Integer deleted) { this.deleted = deleted; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", age=" + age + ", deleted=" + deleted + '}'; } }
2.3 在domain目录下创建hibernate配置文件,表与实体类的关系
类名.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"> <hibernate-mapping package="cn.getword.domain"> <class name="User" table="tb_user"> <!--建立字段关系--> <!--指定主键id--> <id name="id" column="id" type="int"> <!--自增长策略 native:本地生成策略 assigned: --> <generator class="native"></generator> </id> <!--配置属性--> <property name="username" column="username" /> <property name="age" column="age" /> <property name="deleted" column="deleted" /> </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"> <!-- ORM元数据 表对象关系映射文件 package : 配置该配置文件中类所在的包. --> <hibernate-mapping package="com.itheima.a_hello" > <!-- class: 配置实体与表的关系 name : 填写实体的完整类名 table: 与实体对应表的名称 dynamic-insert:动态插入 默认值是false true=>如果字段值为null,不参与insert语句 dynamic-update:动态更新 默认值"false" true=> 没改动过的属性,将不会生成到update语句中 --> <class name="User" table="t_user" > <!-- id: 配置实体与表中 id对应 name: user对象中标识主键的属性名称 column: 主键在表中的列名 length: 列的数据长度 unsaved-value(不常用): 指定主键为什么值时,当做null来处理. access(强烈推荐不要用):field 那么在操作属性时,会直接操作对应的字段而不是get/set方法 --> <id name="id" column="id" length="255" > <!-- generator:主键生成策略 1.increment 数据库自己生成主键. 先从数据库中查询最大的ID值,将ID值加1作为新的主键 2.identity 依赖于数据的主键自增功能 3.sequence 序列,依赖于数据中的序列功能(Oracle). 4.hilo(纯了解,永远用不到) : Hibernate自己实现序列的算法,自己生成主键. (hilo算法 ) 5.native 自动根据数据库判断,三选一. identity|sequence|hilo 6.uuid 生成32位的不重复随机字符串当做主键 7.assigned 自己指定主键值. 表的主键是自然主键时使用. --> <generator class="uuid"></generator> </id> <!-- property : 实体中属性与表中列的对应 name : 实体中属性名称 column : 表中列的名称 length : 数据长度 precision: 小数点后的精度 scale: 有效位数 insert(一般不用): 该属性是否加入insert语句. update(一般不用): 该属性是否加入update语句. not-null : 指定属性的约束是否使用 非空 unique : 指定属性的约束是否使用 唯一 --> <!-- type: 表达该属性的类型 可以用三种方式指定属性 java类型 数据库类型指定 Hibernate类型指定 java.lang.String varchar string --> <property name="name" column="name" update="true" type="string" ></property> <property name="password" column="password"></property> <property name="sal" column="sal" precision="2" scale="3" ></property> </class> </hibernate-mapping>
2.4 在src目录下创建hibernate.cfg.xml主配置文件
属性参考:源码包下的project目录下的etc目录下的文件

<!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 元素用于配置Hibernate中的属性 --> <!-- hibernate.connection.driver_class : 连接数据库的驱动 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!-- hibernate.connection.username : 连接数据库的用户名 --> <property name="hibernate.connection.username">root</property> <!-- hibernate.connection.password : 连接数据库的密码 --> <property name="hibernate.connection.password">123</property> <!-- hibernate.connection.url : 连接数据库的地址,路径 --> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/db_hibernate</property> <!-- show_sql: 操作数据库时,会 向控制台打印sql语句 --> <property name="show_sql">true</property> <!-- format_sql: 打印sql语句前,会将sql语句先格式化 --> <property name="format_sql">true</property> <!-- hbm2ddl.auto: 生成表结构的策略配置 update(最常用的取值): 如果当前数据库中不存在表结构,那么自动创建表结构. 如果存在表结构,并且表结构与实体一致,那么不做修改 如果存在表结构,并且表结构与实体不一致,那么会修改表结构.会保留原有列. create(很少):无论是否存在表结构.每次启动Hibernate都会重新创建表结构.(数据会丢失) create-drop(极少): 无论是否存在表结构.每次启动Hibernate都会重新创建表结构.每次Hibernate运行结束时,删除表结构. validate(很少):不会自动创建表结构.也不会自动维护表结构.Hibernate只校验表结构. 如果表结构不一致将会抛出异常. --> <property name="hbm2ddl.auto">update</property> <!-- 数据库方言配置 org.hibernate.dialect.MySQLDialect (选择最短的) --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- hibernate.connection.autocommit: 事务自动提交 --> <property name="hibernate.connection.autocommit">true</property> <!-- 将Session与线程绑定=> 只有配置了该配置,才能使用getCurrentSession --> <property name="hibernate.current_session_context_class">thread</property> <!-- 引入ORM 映射文件 填写src之后的路径 --> <mapping resource="cn/getword/domain/User.hbm.xml" /> </session-factory> </hibernate-configuration>
2.5 入门案例
插入一条数据:
(1)解析hibernate.cfg配置文件
(2)获取sessionFactory
(3)获取session
(4)开启事务
(5)执行操作
(6)提交事务
(7)释放资源

package cn.getword.test; import cn.getword.domain.User; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.junit.Test; public class TestUser { @Test public void test1(){ //解析配置文件 Configuration configuration = new Configuration().configure(); //创建配置对象,加载朱配置文件,路径默认 //获取SessionFactory SessionFactory sessionFactory = configuration.buildSessionFactory(); //获取一个新的session对象 Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction();//开启事务 //执行保存操作 User user = new User(); user.setUsername("张三"); user.setAge(21); user.setDeleted(0); session.save(user); //提交事务 transaction.commit(); session.close(); } }
注意:不能使用new User(){{}}进行初始化。
2. hibernate中常用的对象
Configuration
SessionFactory
使用原则:一个应用中只能有一个,应用创建时创建,结束时销毁。由于它维护的东西比较多,创建和销毁很消耗资源。
Session
Query
2.1 使用HibernateUtil创建单例的SessionFactory,并创建session

package cn.getword.utils; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.Session; public class HibernateUtils { private static SessionFactory sf; static{ //hibernate将所有异常都帮我们转成运行时异常 Configuration conf = new Configuration().configure(); sf = conf.buildSessionFactory(); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { System.out.println("打开数据库连接"); sf.close(); } })); } public static Session getOpenSession(){ Session session = (Session) sf.openSession(); return session; } public static Session getCurrentSession(){ Session session = (Session) sf.getCurrentSession(); return session; } }
3.hibernate的基本增删改查
1.添加
session.save(user);
默认值:数据库表中的字段有默认值
2.查询
- 根据OID:get/load
- SQL查询:SQLQuery
- HQL【hibernate query language】查询:Query
HQL语法:表名换成对应的类名,字段替换成属性
- QBC查询:query by Criteria, 官方推荐使用HQL。QBC将查询语句封装到方法中,效率相对低
- 对象导航查询:表的关系:如一对多
(1)【OID查询】
session.get(User.class, Serilizable ) 必须写id的类型【Integer、Long..】 类型必须一致 :如果查询不到,返回null
session.load(User.class, Serilizable ) :如果查询不到,则跑出异常。
get和load的区别:
load:根据id查询单个实体,返回的是代理对象,默认使用lazy【懒加载】模式-即当真正使用查询结果时,可以对加载模式进行修改。才进行查询。如果查询不到,抛出异常
get:根据id查询,返回POJO对象,是一执行立即查询。加载模式固定
(2)SQLQuery或者Query对象操作

public void get(){ Session session = HibernateUtils.getOpenSession(); Transaction ts = session.beginTransaction(); SQLQuery query = session.createSQLQuery("SELECT * FROM tb_user where id=?"); query.setParameter(0, 1); List<Object[]> list = query.list(); for (Object[] os : list) { System.out.println("----");; for (Object o : os) { System.out.println(o); } } ts.commit(); session.close(); }
(3)HQL查询
- 基本查询:使用Query对象

public void test1(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Query query = session.createQuery("from User"); List<User> users = query.list(); for (User u : users) { System.out.println(u); } transaction.commit(); }
- 条件查询:占位符角标从0开始,
public void test1(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Query query = session.createQuery("from User where id>? and username like ?"); query.setInteger(0,3); query.setString(1, "%admin%"); List<User> users = query.list(); for (User u : users) { System.out.println(u); } transaction.commit(); }
或者:
Query query = session.createQuery("from User where id>:id and username like :username"); query.setParameter("id", 3); query.setParameter("username", "%admin%");
- 排序查询
from User order by age desc
- 分页查询
mysql 通过limit m, n进行分页查询 在HQL中不能使用【不支持limit关键字】
query.setFirstResult(2); //通用性
query.setMaxResults(3);
- 统计查询
在mysql中,sum,count,avg,max ,min
select count(*) from User
query.uniqueResult() :该方法只能用于只有一条查询结果
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Query query = session.createQuery("select count(*) from User"); Object o = query.uniqueResult(); long rs = (long) o; //Long类型 System.out.println(rs); transaction.commit();
- 投影查询
只需要用到部分字段:需要在实体类中创建对应的构造方法
select new User(id, username) from User;
(3)QBC查询
- 基本查询
Criteria criteria = session.createCriteria(User.class); List list = criteria.list();
- 条件查询
Criteria criteria = session.createCriteria(User.class); criteria.add(Restrictions.gt("id", 2)); List list = criteria.list();
- 离线查询
从前台获取的请求参数比较多,为了更好地向dao层传递参数,将参数封装起来,可以拼接成SQL语句向后台传递。

@Test public void controller(){ //离线对象 DetachedCriteria dc = DetachedCriteria.forClass(User.class); dc.add(Restrictions.gt("id", 2)); List<User> users = service(dc); for (User u : users) { System.out.println(u); } } public List<User> service(DetachedCriteria dc){ List<User> list = null; Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); try{ list = dao(dc); transaction.commit(); }catch (Exception e){ e.printStackTrace(); transaction.rollback(); } return list; } public List<User> dao(DetachedCriteria dc){ Criteria c = dc.getExecutableCriteria(HibernateUtils.getCurrentSession()); return c.list(); }
service层的事务管理可以使用AOP思想进行改装。
(4)对象导航查询
3. 更新
session.update(user)

Session session = factory.openSession(); try { Transaction ts = session.beginTransaction(); String sql = "update student set name=?, age=?, password=? where id=?"; SQLQuery query = session.createSQLQuery(sql); query.setParameter(0, user.getName()); query.setParameter(1, user.getAge()); query.setParameter(2, user.getPassword()); query.setParameter(3, user.getId()); int rs = query.executeUpdate(); ts.commit(); session.close(); return 1; } catch (Exception e) { return 0; }
4. 删除
session.delete(user)
4.事务提交和回滚
一般事务管理是放在业务逻辑层的。
ts.rollback()

public void add(){ Session session = HibernateUtils.getOpenSession(); Transaction ts = session.beginTransaction(); try{ User user = new User(); user.setUsername("王五"); user.setAge(23); Serializable rs = session.save(user); System.out.println(user); ts.commit(); }catch (Exception e){ ts.rollback(); }finally { session.close(); } }
5.c3p0连接池配置
1.导入c3p0jar包
2.在主配置文件中配置c0p3连接池
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
6.实体类的编写规范
遵循JavaBean规则
bean:开发中可重用的组件
JavaBean:在java开发中可重用的组件,例如:service、dao、实体类等。
规范:
都是public
一般都实现序列化接口
成员变量都是被private修饰的
提供set、get方法
都有默认的无参构造
7.OID以及hibernate主键生成 策略
1.OID object identifier
用于标识对象的唯一性。 OID就是映射文件的主键对应的属性。
hibernate把OID一致的对象认为是同一个对象。在同一session中,不允许出现相同OID的对象。
因此,主键一般不由自己管理,交个hibernate管理。
2.hibernate主键生成策略
native :适用于代理主键
identity:自增长。适用于代理主键
increment:先查询出最大id,然后加一作为id,在高并发环境中容易出问题。基本不使用
sequence:根据数据库序列生成标识符,例如Oracle。 适用于代理主键。
uuid:
assigned:id有java代码指定,适用于自然主键。例如学号:201509001 一般都是有规律的
代理主键和自然主键的区别;
代理主键:只是用来区分数据库记录,不涉及业务信息,没特殊意义。例如:1,2,3,4,5.......
自然主键:不仅要用来区分数据库记录,还设计业务信息,例如学号、员工编号、订单编号。一般自然主键都是有规律的。
end