频道栏目
首页 > 资讯 > Java > 正文

Java.Web学习笔记 Hibernate

16-08-06        来源:[db:作者]  
收藏   我要投稿

Hibernate

它是一种ORM框架,全称是Object-Relative Database-Mapping,在Java对象和关系数据库之间建立某种映射,以实现直接存取Java对象(一般为POJO)。

Hibernate原理

1. 通过POJO和实体数据库的映射配置自动生成相应的SQL语句,从控制台输出可以看到Hibernate生成的SQL语句,这个是动态生成的,到时候只要修改配置文件就可以了。

2. 注解配置

注解的方式与xml很很多类似:

首先是需要加入4个jar包:hibernate-commons-annotations.jar 、 hibernate-annotations.jar

ejb3-persistence.jar 、 hibernate-jpa-2.0-api-1.0.1.Final.jar

下面是不同的地方:

(1):hibernate.hbm.xml 文件中把引用:xxx.hbm.xml改为引用实体类:

即把:

改为:

(2):获取SessionFactory方式发生了变化:

即:由SessionFactory sf = new Configuration().configure().buildSessionFactory()

改为:SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory()

(3):注解方式不需要在xxx.hbm.xml把实体类与表进行映射。而采用在实体类中进行注解。

注意:(1):如果实体类属性名与表字段名不一致的时候,要么都注解在属性前,要么都注解在get方法前。不能部分注解在属性前,部分注解在方法前。

(2):如果实体类属性名与表字段名一致的时候,可以部分注解在属性前,部分注解在方法前。

(3):如果在实体类中某些属性不注解:(属性和get都不写注解),默认为表字段名与实体类属性名一致。

(4):如果实体类的某个成员属性不需要存入数据库中,使用@Transient 进行注解就可以了。即类似于:(xxx.hbm.Xml配置中的某些字段不写(就是不需要对这个成员属性进行映射))

(5):表名称可以在实体类前进行注解。

(6):所有这些注解在:javax.persistence包下。而不是在hibernate包中。

Hibernate体系结构

1. 这是Hibernate在程序中的位置

几个常用的概念SessionFactory、Session与Transaction,这里的Session不同于Servlet中的HttpSession。

(1) SessionFactory: 一个线程安全的Session工厂类,能为不同线程生成不同的Session。

(2) Session:Session代表用户的一次操作。Session的生命周期很短,在操作开始的时候通过执行SessionFactory.openSession()生成,通过session.close()关闭。

(3) Transaction:Transaction代表一次事务

可持久化对象状态 有三种状态

(1) 临时状态(Transient):还没被保存进数据库之前

(2) 持久化状态(Persistent):在保存进数据库之后或者从数据库加载

(3) 分离状态(Detached):对象曾经持久化,但是现在已经离开了Session,有id但是无法执行数据库操作了

(4)

Hibernate配置参数 配置文件Hibernate.properties, hibernate.cfg.xml

两个都能配置参数,不同的是cfg.xml不需要Hibernate前缀,而.properties文件需要 还可以运行的时候编程式配置、运行时配置

加载xml

Configuration cfg=new Configuration()

.addResource(“xxx/xxx/xxx.xml”);

加载class

Configuration cfg=new Configuration()

.addClass(xxx.xxx.xxx.class);

通过setProperty(xxx,bbb)可以设置Hibernate参数 获取SessionFactory

通过Configuration获取SessionFactory

SessionFactory sessionFactory= cfg.buildSessionFactory();

注意一个SessionFactory只能维护一个数据库,如果需要多个那就多开几个SessionFactory。 获取、断开数据库连接

Session session=sessionFactory.openSession();

session.close(); 它可以使用c3p0数据连接池或者JNDI数据连接池

只要在配置文件中设置了hibernate.c3p0.*的参数,Hibernate就自动使用c3p0连接池了

可选配置参数

Test流程

//1.创建一个SessionFactory对象
SessionFactory sessionFactory=null;

    //创建一个Configuration对象:对应hibernate的基本配置信息和对象关系映射信息
    Configuration configuration=new Configuration().configure();

    //hibernate4.0之前就是这样创建

// sessionFactory=configuration.buildSessionFactory();

    //现在需要创建一个ServiceRegistry对象:hibernate 4.x新添加的对象
//hibernate的任何配置和服务都要在该对象中注册才能有效
        org.hibernate.service.ServiceRegistry serviceRegistry=new ServiceRegistryBuilder()
        .applySettings(configuration.getProperties())
        .buildServiceRegistry();


        sessionFactory=configuration.buildSessionFactory(serviceRegistry);


        //2.创建一个Session对象
        Session session=sessionFactory.openSession();


        //3.开启事务
        Transaction transaction=session.beginTransaction();

        //4.执行保存操作
//      News news=new News("Java","JanathonL",new Date(new java.util.Date().getTime()));
//      session.save(news);

        News news2=(News) session.get(News.class, 1);
        System.out.println(news2);

        //5.提交事务
        transaction.commit();

        //6.关闭Session
        session.close();

        //7.关闭SessionFactory对象
        sessionFactory.close();
根据数据库方言进行使用不同的特定sql语句 Configuration类
Hibernate运行的底层信息:数据库相关
包含持久化类和数据表的映射文件(*.hbm.xml文件)
创建
属性文件(hibernate.properties)
Configuration cfg=new Configuration();
Xml文件 (hibernate.cfg.xml)
Configuration cfg=new Configuration().configure();
带参数的自己更改的其他文件类型。
就使用重载方法。 SessionFactory接口
线程安全
很消耗资源
Hibernate4里面需要在ServiceRegistry注册才能创建这个东西 Session是一个应用程序和数据库之间交互操作的一个单线程对象,相当于JDBC的Connection
? 常用方法
Get()load()
Save()update()…
? 缓存
Session先查询session缓存有没有,只要session不关闭,也没有清理缓存的操作时,那缓存不会消失,如果缓存中没有那么就去查数据库,这也是一级缓存
? 清理缓存
flush() 如果事务提交之前检查缓存数据和数据库是不是相同的,不相同就会把数据库刷新,flush()函数只是提交sql语句 在Transaction的commit()方法中:先调用session的flush方法,再提交事务 Flush()方法可能会提交sql语句,但是不会提交事务 注意:
1) 如果在session未提交事务或者显示的调用session.flush()之前,也有可能会进行flush()操作。
执行HQL或者QBC查询,会先进行flush()操作,得到数据表的最新的记录
如记录的ID是由底层数据库使用自增的方式生成的,则调用save()方法后,会立即发送insert语句,因为save方法后,必须保证这个ID是存在的。
2)
Refresh()方法,保证缓存中的数据都是最新的状态,和数据库一致,会强制发送select语句,如果必要的话要设置一下隔离级别。
Clear()方法清理缓存
Update()方法,如果正常持久化对象不需要显示调用该方法,如果游离对象一定要显示调用
注意: 无论要更新的游离对象和数据表的记录是不是一致,还是会发送Update语句
如何能让update方法不是盲目触发update语句?在.hbm.xml 文件的class节点设置一个select-before-update=true就可以了 如果数据表中没有对应记录但还是调用了update方法,会抛异常,游离对象可以改ID 当update()方法关联一个游离对象时,如果在Session的缓存中已经存在相同的OID持久化对象会抛出异常,因为在Session缓存中不能有两个相同OID的对象
/**
doWork可以就用JDBC那一套进行存储
*/
/**
delete:执行删除操作,只要OID和数据表中的一条记录对应,就会准备执行delete操作 若OID 在数据表中没有对应的记录则抛出异常
可以通过设置hibernate 配置文件 hibernate.use_identifier_rollback 为 true 使得删除的对象的ID为null
delete的删除还是要flush的时候才会真的发送update语句
*/

/** 注意: 若OID不为null,但是数据表还没有和其他记录对应,会抛出一个异常 OID值等于id的unsaved-value 属性值的对象,也会被认为是游离对象
*/
/**
get VS load
1. 执行get方法:会立即加载对象, 而执行load方法,如果不使用该对象,则不会立即执行查询操作,而返回一个代理对象 get是立即检索,load 是延迟检索。
若数据表中没有对应的记录,且Session也没有关闭,同时需要使用对象时 get返回null load如果不用该对象的任何属性,没问题; 如果要初始化了,抛出异常
load方法可能会抛出LazyInitializationException:在需要初始化代理对象之前,已经关闭了Session就可能会抛异常。
*/
/**
persist()方法也会insert操作
和save()的区别 在 persist 方法之前,如果对象已经有ID了则不会执行insert,而会抛出异常。
*/

Transaction事务,代表一次原子操作
常用方法
Commit()
Rollback()

Hbm2ddl.auto

Hibernate.cfg.xml
配置数据库连接的各种属性和Hibernate运行所需要的属性
? C3P0数据源配置
(1) 导入jar包
(2) 加入配置: *.hbm.xml
? 注意表名不能和数据库关键字重复
? 根目录是hibernate-mapping 可选属性package,如果类名没有全部写出来那么就用包名作为前缀
? 一个.hbm.xml可以配置多个class但是推荐就只配置一个
? dynamic-update=”true”动态生成update语句,只update被修改的部分
? identity生成器
(1) increment:先读最大的,然后加一,这会有并发问题
(2) native:跨平台,自动选择identity,sequence,hilo中的一种
(3) sequence:利用底层数据库提供的序列来生成标识符
(4) hilo:Hibernate按照一种high/low算法生成标识符。所有数据库系统都支持,因为它不依赖底层数据库
? property
(1) access:如果是property那么就是使用getter/setter方法,如果是field那么就是通过反射设置对象
(2) type可以使用Java类型或者Hibernate类型,不写的话会使用反射机制进行确定
(3) formula=“(sql)”英文括号不能省略
Sql表达式中的列名和表名都应该和数据库对应,而不是和持久化对象的属性对应,如果需要在formula属性中使用参数,直接使用where cur.id=id的形式。
? 映射Java的时间,日期类型
Java.util.Date是java.sql.Date, java.sql.Time, java.sql.Timestamp的父类,所以设置持久化类的类型是设置为java.util.Date
可以通过property的type属性使用Hibernate属性time,date,timestamp来映射不同的属性
如果希望精确映射sql类型可以使用sql-type类型,在column里的
? 组件映射,也就是如果一个表对应多个类,这就用到component了,没有id的那个类属于有id的那个类
? Many to one的时候
(1) 先插入n的一端再插入1 的一端,会多出UPDATE语句
因为插入多的一端的时候,无法确定1的一端的外键值,所以会额外发送UPDATE语句
(2) 推荐先插入1的一端。
(3) 查询对象
//1. 若查询多的一端的一个对象,则默认情况下只查询了多的一端的对象,
        //而没有查询关联的一的那一端的对象
        Order order = (Order) session.get(Order.class, 1);
        System.out.println(order.getOrderName());

        //2. 在需要使用到关联的对象是,才发送对应的sql语句,这叫做延迟加载
        Customer customer=order.getCustomer();
        System.out.println(customer.getCustomerName());

        //3. 在查询Customer对象时,用多的一端导航到1时,
        //若此时session已被关闭,则默认情况下
        //一定会发生软加载异常

        //4. 获取Or

der对象时,默认情况下,其关联的Customer对象是一个代理对象!
? 双向many to one的时候

        //3条insert,2条update
        //因为1的一端和n的一端都维护关联关系,所以会多出UPDATE
        //可以在1的一端的set节点指定inverse=true,来使1的一端放弃维护关联关系
        //1-n关系中,将n方设为主控方,会比较好
        //还是建议先插入1的一端
        session.save(customer);
        session.save(order2);
        session.save(order1);

Get方法

//1. 对n的一端的集合使用延迟加载
        Customer customer=(Customer) session.get(Customer.class, 1);
        System.out.println(customer.getCustomerName());
        //2. 返回的多的一端的集合是Hibernate内置集合类型
        //该类型具有延迟加载和存放代理对象的功能
        System.out.println(customer.getOrders().getClass());
        //3.可能会抛出懒加载异常,当session关闭了,但是又去调用Order的值
        //4.在需要时候集合中元素的时候进行初始化
定义set时
/**
     * 1.声明集合类型是,需要使用接口类型,因为Hibernate在获取集合类型时,
     * 返回的是Hibernate内置的集合类型不是JavaSE一个标准的集合
     * 2.需要把集合进行初始化,防止发生空指针异常
*/
private Set orders=new HashSet();

? 一对一的关联关系

//在查询没有外键的实体对象时,使用的是左外连接查询,一并查询出其关联的对象
        //并进行初始化
        Manager mgr=(Manager) session.get(Manager.class, 1);
        System.out.println(mgr.getMgrName());
//1. 查询有外键的一端,默认情况下对关联属性使用懒加载
        //2. 所以会出现懒加载异常的问题
        Department department=(Department) session.get(Department.class, 1);
        System.out.println(department.getDeptName());
        //3. 查询Manager 对象的连接条件应该是dept.manager_id=mgr.manager_id
        //而不是dept.dept_id=mgr.manager_id
        Manager mgr=department.getMgr();
        System.out.println(mgr.getMgrName());

配置文件
没有外键的一端


        
        

有外键的一端


        

继承映射
1. Subclass方式

/**
* 插入操作:
* 1. 对于子类对象只需把记录插入一张数据表
* 2. 辨别者列由Hibernate自动维护
*/
/**
* 缺点:
* 1. 使用了辨别者列
* 2. 子类独有的字段不能添加非空约束
* 3. 若继承层次比较深,则数据表的字段也会比较多
*
*/
/**
* 查询:
* 1. 查询父类记录只需要查询一张数据表
* 2. 查询子类记录,也只能查询一张数据表
*/
配置文件中,只要配置父类的hbm.xml即可


        
        
        
            
        
Joined-subclass

/**
* 优点:
* 1. 不需要使用辨别者列
* 2. 子类独有的字段能添加非空约束
* 3. 没有冗余字段
*
*/
/**
* 查询:
* 1. 查询父类记录,做一个左外连接查询
* 2. 查询子类记录,做一个内连接查询
*/

/**
 * 插入操作:
 * 1. 对于子类对象需把记录插入多张数据表
 */

配置文件中,只要配置父类的hbm.xml即可


    
    
Union-subclass

/**
* 优点:
* 1. 不需要使用了辨别者列
* 2. 子类独有的字段能添加非空约束
*
* 缺点:
* 1. 存在冗余的字段
* 2. 若更新父表的字段,那么更新比较麻烦,两边都要更新
*/
/**
* 查询:
* 1. 查询父类记录需把父表和子表记录汇总到一起才能做查询,性能稍差
* 2. 查询子类记录,也只能查询一张数据表
*/
/**
* 插入操作:
* 1. 对于子类对象只需把记录插入一张数据表
*/
配置文件中,只要配置父类的hbm.xml即可


            

检索数据
1. 类级别的检索数据
就是在hbm.xml文件中的class中加入lazy的属性,
注意这是用load方式,如果get方式那都是立即检索
如果为false就是立即检索
如果为true就是延迟检索
2. 一对多和多对多的检索策略
//set的lazy属性
//System.out.println(customer.getOrders().size());
//1. 1-n 或者n-n 的集合属性默认使用懒加载检索策略
//2. 可以通过设置set 的lazy属性来修改默认的检索策略,默认为true,
//并不建议设置为false
//3. lazy还可设置为extra。 增强的延迟检索,该取值会尽可能延迟集合初始化的时机。
//set 元素的batch-size 属性:设定一次初始化的set集合的数量。
//set 集合fetch属性: 确定初始化orders集合的方式
//select或者subselect决定初始化orders的查询语句的形式
//join决定orders集合被初始化的时机
//1. 默认值为select。通过正常的方式来初始化set元素
//2. 可以取值为subselect,通过子查询的方式来初始化所有的set集合,子查询
//作为where 子句的in 的条件出现,子查询查询所有1的一端的ID,此时,lazy有效,但是
//batch-size无效了
//3. 若取值为join,则
//3.1 在加载1的一端对象时,使用迫切左外连接(使用左外链接进行查询,且把集合属性进行初始化了)的方式检索的集合属性
//3.2 忽略lazy属性
//3.3 HQL忽略fetch=join的取值
多对一
//1. lazy取值为proxy和false分别代表对应的属性采用延迟检索和立即检索
//2. fetch取值为join,表示使用迫切左外连接的方式初始化n关联的1的一端的属性
//忽略lazy属性
//3. batch-size,该属性需要设置在1那一端的class元素中:
//作用:一次初始化1的这一段代理对象的个数。
HQL
1. 基础

// 1. 创建Query对象

// 基于命名的参数
        String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
    // 2. 绑定参数
query.setFloat("sal", 10).setString("email", "%a%");
        // 基于位置的参数
        String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept=?"
                + "Order by e.salary";
        Query query = session.createQuery(hql);
        // 2. 绑定参数
        // Query对象调用setXxx方法支持方法链的编程风格
        Department dept = (Department) session.get(Department.class, 4);
        query.setFloat(0, 10).setString(1, "%a%").setEntity(2, dept);
    // 3. 执行查询
        List emps = query.list();
        System.out.println(emps.size());
分页查询
setFirstResult() 定义第一个要查的位置
setMaxResult() 定义每个页的大小 命名查询
可以把hql语句放到hbm.xml文件中,使用getNamedQuery()方法进行查询 投影查询
只查部分属性,也可以直接select new Employee(e.name , e.id),当然要写出对应的构造方法,同时写上默认方法 报表查询,也就是使用group by HQL(迫切)左外连接
? 迫切左外连接
//Left Join Fetch表示迫切左外连接
//1. list()方法返回的集合存放的实体对象引用,
//每个Department对象关联的Employee集合都已经被初始化了
//查询结果可能有重复元素,可以通过一个HashSet来过滤重复元素
? 左外连接
//左外连接使用left join
//list方法返回的是集合中存放的是对象数组类型
//根据配置文件来决定Employee集合的检索策略
//如果仅希望查Department对象,可以在HQL查询语句中使用Select关键字 QBC和本地sql比较简单 二级缓存
? 一级缓存是session缓存,Hibernate来管理
? 二级缓存是SessionFactory是进程范围的缓存
内置缓存:Hibernate自带的,不可卸载的
外置缓存(二级缓存):一个可配置的缓存插件,默认不启用,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或者硬盘。
适合放入二级缓存的数据:
很少被修改的,经常被查询的数据,不是很重要的数据,允许出现偶尔的并发问题。
(1) 使用hibernate二级缓存的步骤
1) 加入二级缓存插件的jar包和配置文件
2) 配置hibernate.cfg.xml
I. 配置启用hibernate的二级缓存
true
II. 配置hibernate二级缓存使用的产品

        org.hibernate.cache.ehcache.EhCacheRegionFactory

III. 配置对哪些类使用hibernate的二级缓存

其实也可以在.hbm.xml文件中配置对哪些类使用二级缓存,及二级缓存的策略是什么。
(2) 集合级别的二级缓存
I. 配置对集合的二级缓存,可以在hibernate中也可以在hbm.xml中配置
hibernate中


hbm.xml中

II. 注意还要配置集合中元素对应的持久化类也使用二级缓存,否则要多出n条SQL语句
(3) Echcache的配置文件:ehcache.xml


(4) 查询缓存:默认情况下,设置的缓存对HQL 及QBC查询是无效的,但可以通过以下方式使其有效
I. 在hibernate配置中声明开启查询缓存

true

II. 调用Query或Criteria的setCacheable(true)方法
III. 查询缓存依赖于二级缓存

相关TAG标签
上一篇:怎么样能够删除现有Word文档页码
下一篇:Java 异常设计最佳实践
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站