1.hibernate初始化
在hibernate.cfg.xml文件中配置,可根据Demo进行调整。
hibernate.show_sql为true,显示SQL操作语句。
hibernate.hbm2ddl.auto属性:
为create-drop,表示自动创建表和删除表。
为update,表示会更新表,但不会删除表。
为create,每次自动创建表,如果存在则删除。
示例代码:
Configuration cfg = new Configuration(); cfg.configure();//默认classpath下,可指定其它路径下的文件 SessionFactory sf = cfg.buildSessionFactory(); Session session = sf.openSession(); session.save(po);
2.hibernate一些要点
(1)hibernate需手动的开启事务并提交,否则不去更新数据(不同于JDBC)
(2)hibernate的开发流程(object->mapping->db,官方推荐),
但现实中一般使用较多的为(db->生成mapping和po)
(3)hibernate无法管理带参数的构造函数,须有一个默认的不带参数的构造函数。
(4)po最好非final,对懒加载有影响。
(5)配置文件如果列名不指定,则默认与类属性名一致。
(6)Session关闭应放在finally块中,确保连接被关闭。
(7)懒加载是通过asm和cglib二个包来实现的,hibernate默认都使用懒加载。
(8)Session是非线程安全的,生命周期较短,代表一个和数据库的连接,如果长时间打开,
会长时间占用内存和数据库连接。SessionFactory是线程安全的,一个数据库对应
一个SessionFactory,在系统生命周期内有效。
3.Session的相关方法
(1)get(User.class,”id”),根据主键Id查得用户记录。
(2)load(User.class,”id”),功能同get,主要区别在于load在第一次使用对象时
才会去查询数据库,返回的对象永远不会为空,即使查询不到数据也会为一个空对象。
(3)persist(po),与save最大的区别在于没有开启事务时save会插入语句,然后执行
完后回滚,而persist不会执行插入语句。
(4)saveOrUpdate(po),会自动根据po的Id和version的值来确定是save或update,
如果是瞬时对象(没有ID)用save,脱管对象(有ID)用update,执行完后po对象变为持久对象。
(5)merge(po),同saveOrUpdate一样,区别在于执行完后PO对象还是脱管的。
(6)delete(po),将持久对象变成瞬时对象,删除对象时必须根据对象来删除,可用new一个空对象,
将主键ID赋值,前提是对象的其它属性没有非空或其它约束。
(7)evict(),close(),clear()将持久变成脱管。
(8)update(),saveOrUpdate(),lock()将脱管变成持久的。
(9)flush(),将一级缓存与数据库同步,一般不自己调用,让hibernate自动维护,hibernate一般会延迟处理。
(10)save(),一般自增长的主键,save后会马上插入数据库,大批量操作数据时可能会造成内存溢出,
解决办法如下:
例: for(int i=0;i<100000;i++){ session.save(po); if(i%50==0){ session.flush(); session.clear(); } }
4.PO对象状态
(1)瞬时对象(transient),new出来没有与session关联的对象,会自动被回收销毁。
(2)持久对象(persistent),数据库中有数据与之对应,当前与session有关联,
并且session没有关闭,事务没有提交。
(3)脱管对象(detached),数据库中有数据与之对应,但当前没有session与之关联,或称为游离对象。
(4)在session域中,如果对象发生变化,则自动会调用update语句(在提交事务时),
在脱管状态中,如果对象发生变化,则需显示地调用update(po)来执行update语句。
5.HQL查询
例:Query query = session.createQuery("from User as user where user.name=?"); query.setString(0, "chen"); List list = query.list();// 获取查询列表 query.uniqueResult();//在确定查询结果只有一条时使用 //如果hql为“from User as user where user.name=:name” //则应为: query.setString("name", "chen"); query.setFirstResult(0);//首记录 query.setMaxResults(10);//取10条记录(可用于分页)
6.SQL查询
例:Query query = session.createSQLQuery("select * from User"); List rs = query.list();
返回的rs结果为一对象数组。
如果添加.addEntity(User.class),则返回的结果为User对象List.
不足:如果采用SQL查询,则Hibernate的数据库移植性可能会受到影响,因为每种数据库的查询SQL有差别。
7.Hql,Sql查询映射文件配置
一般不建议将hql写在业务中,而是在映射文件中进行配置,然后通过命名查询调用,方便以后修改,维护。
例: <query name="getUserByUserId"> <![CDATA[from User where userId==userId]]> </query>
此配置语句可放在class里面进行定义,也可放在class外面,需保证名字在所有配置文件中是唯一的。
Query query = session.getNamedQuery("getUserByUserId"); query.setString("userId", userId); List list = query.list();
如果getUserByUserId命名查询放在class配置里面,则获取时需将包名和类名完整写入,
例:com...User.getUserByUserId.
8.Criteria条件查询(面向对象查询)
例 : Criteria c = session.createCriteria(User.class); // 约束条件为等于,还有大于,小于,like等 c.add(Restrictions.eq("name", "chen"));
9.离线查询(动态构造查询语句)
DetachedCriteria dc = DetachedCriteria.forClass(User.class) dc.add(Restrictions.eq("name", name)); dc.add(Restrictions.eq("age", age)); Criteria c = dc.getExecutableCriteria(session); List rs = c.list();
10.Iterator查询
Query query = session.createQuery("from user"); Iterator<User> users = query.iterate();
iterator()方法会先查找Id,再根据ID查找记录,如果缓存中没有数据,会有N+1次查询数据库。
懒加载也会造成N+1次的查询,先查找当前属性,再到关联数据库逐一查找。
11.Hibernate事件监听
拦截器与事件都是Hibernate的扩展机制作,拦截器是老的实现机制,现在改成事件监听机制。
例:class SaveListener implements SaveOrUpdateEventListener{ public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException { if(event.getObject() instanceof User){ // 具体处理逻辑 } } }
配置信息:
<event type = "save"> <listener class="com.icsshs.smis.listener.SaveListener"> <listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener"> </event>
12.Hibernate对象关联映射
(1)<id name=””></id> 用来映射主键,有以下几种方式:
1.native,会自动根据数据库选择自增长的方式。
2.assigned,手工分配标志符,除非编码有意义时,否则不建议使用。
3.composite-id,联合主键,也不建议使用,引用外键时性能不高。
(2)多对一(例:员工->部门),在员工对象中加部门外键
<many-to-one name="depart" column="depart_id"/>
(3)一对多(例:部门->员工),在部门对象中创建
<set name="employeeSet"> <one-to-many class="Employee"/> <key column="depart_id"/> </set>
(4)一对一(例:用户->身份证)
例:Person类:id,name,IdCard;(id为主键)
IdCard类: id,name,Person(id为主键,也是外键)
Person类映射:
<id name="id" column="person_id"> <generator class="native"/> </id> <one-to-one name="idCard" />
IdCard类映射:
<id name="id" column="personID"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <!-- constrained="true"添加外键约束 --> <one-to-one name="person" class="Person" constrained="true"/>
一对一的另外一种建立关系,则从表有自己的主键ID,并建立一外键与主表关联(需限制为唯一)
例:主表(person)的关联映射需改为
<one-to-one name="idCard" property-ref="person" />
从表(idCard)的关联映射需改为
<many-to-one name="person" unique="true" column="person_id"/>
(5)多对多(例老师-学生)
操作和性能不太理想,实际使用中最好转换成一对多的对象模型,创建中间表,转换成两个一对多。
例:Teacher:id,name,studentSet;
Student:id,name,TeacherSet;
Teacher表映射:
<set name="studentSet" table="teacher-student"> <many-to-many class="Student" column="student_id" /> <key column="teacher_id"></key> </set>
Student表映射类似于Teacher.
在更新数据时,只需单方进行set(),双方都进行set更新时会出错。
(6)在session范围外,要获得关联的对象,如果没有配置延迟加载false,则取不到数据,
可用Hibernate.initialize(emp.getDepart()),初始化员工的部门对象。
(7)Cascade用来说明当对主对象进行某种操作时是否对其关联的从对象也进行类似的操作。
常用的有:none,all,save-update,delete,lock,refresh,evict,replicate,
persist,merge,delete-orphan。一般对<many-to-one>,<many-to-many>不设置级联,
而在<one-to-one>和<one-to-many>中设置级联。
(8)一对一关系中主对象查询时默认不使用懒加载,使用左外连接查询,而从对象查询时使用懒加载。
(9)fetch是指以什么方式抓取数据,fetch=”join”,通过关联表一次性把数据都查出来,
懒加载无效,默认情况下都是select.
当使用一对多或多对多时,尽量不设置fetch=”join”,可能会关联多张表,性能差。
(10)Lazy是指什么时候抓取数据,lazy=”false”则懒加载无效,查出所有关联的数据,
默认情况下lazy=”proxy”,即使用对象时才查询。当使用懒加载时,访问代理对象的getId()
和getClass()不会去查询访问数据库。当设置fetch=”join”时,lazy的配置失效,没有意义。
13.Hibernate缓存
(1)首先从缓存中取数据,如果不存在则从数据库取数据,然后放入缓存。在更新对象时,将对象从缓存中移除。
(2)一级缓存,属于session级共享,session中存在一级缓存,当session关闭后,一级缓存也就不存在了,
当查找同个对象时,需再次查找数据库。Save,update,saveOrUpdate,load,get,list,iterate,lock
这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能内存溢出,
可以用evict,clear方法清除缓存中的内容。
Query()方法每次都从数据库查询,不会从缓存中取数据。
(3)二级缓存,属于sessionFactory级共享,可以被所有的session共享,hibernate二级缓存交给第三方实现,
配置如下:
例:<session-factory> <!--开启二级缓存--> <property name="hibernate.cache.use_second_level_cache"> true </property> <!-- 设置缓存提供者,相关第三方缓存配置文件需放在classpath下 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> </session-factory>
还需告诉hibernate哪些类需要缓存,一般是较少修改的使用缓存,频繁修改的不使用缓存。
如果想对User类进行缓存, 有以下两种方式:
方式(一):可在hibernate主配置文件中配置:
<session-factory> <class-cache class="com.entity.User" usage="read-only"/> </session-factory>
方式(二):在具体对象映射文件中配置:
<class name="com.entity.User" table="t_user"> <cache usage="read-only"/> <id name="id"> <generator class="native"/> </id> </class>
Usage为使用策略,主要有以下几种方式:
1.read-only,在确保对象不修改时使用。
2.read-write,修改时同步缓存,保证并发读写时正确性,但效率较低。
3.nonstrict-read-write,非严格的读写,比第二种方式效率较高,但不考虑并发操作。
4.transactional,较少使用,用于事务性控制。
(4)二级缓存的清理,通过sessionFactory.evict()进行清理。
(5)使用缓存的条件:
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。
(6)StatelessSession接口,是无状态的session,不和一级缓存,二级缓存交互。
14. 事务管理
事务控制一般应放在三层架构的业务逻辑层。
(1)JDBCTranction,一个SessionFactory对应一个数据库。
(2)JTATranction,跨数据库的事物,由应用JTA容器实现。(tomcat不提供)。
(3)OpenSessionInview,主要解决以下问题:
1.解决事务问题。事务在过滤器打开,在页面渲染后,执行过滤器后关闭。不在业务层进行控制。
2.解决懒加载问题。
存在以下问题:
1.一级缓存一直保留到页面渲染后才释放,占用内存较大。
2.数据库连接长时间没释放,并发大操作时有问题。
(4)悲观锁:当读取数据时加上锁,修改提交后释放锁。
(5)乐观锁:给数据库加上版本号进行控制。在hibernate中具体应用如下:
1.给对象加上版本号.(int型ver属性)
2.在hibernate中配置。<version name=”ver”/>,也可用
<timestamp name=”ver”/>来控制,但精确度有限。
3.在数据库中加上版本号字段。
4.Hibernate在更新数据时会自动读取版本号和更新版本号,同时读取时后面提交的会报错。