http://hi.baidu.com/hking1987/blog/category/Java/index/1
http://blog.youkuaiyun.com/songmin3121/category/546763.aspx
一对多的解释:
关联关系的本质是将关联关系映射到数据库中。关联关系在对象模型中体现为内存中的一个或多个引用。 一对多关系: 一对多关系 分为 “ 单向一对多/多对一关系 ” 和 “ 双向多对一 ” 关系。
“ 单向一对多/多对一关系 ” 只需在 “ 一 ” / “ 多 ” 方进行配置,
“ 双向多对一关系 ” 需要在关联双方均加以配置。 双向多对一关联就是在多方和一方都进行配置,并在 “ 一 ” 方通过属性inverse="true" 设置控制关系反转
注:单向多对一关联是最常见的单向关联关系。
注:双向多对一关联是最常见的双向关联关系。双向多对一关联实际上是 “ 多对
一 ” 与 “ 一对多 ” 关联的组合。
多对一及一对多关联映射的区别(单向):
不管多对一还是一对多,都是在" 多" 的一端添加一个外键指向" 一" 的一端,只不过是多对一是在多的一端为其自己添外键,而一对多则是在一的一端为多的一端添加外主键。
模型:一个用户组(group )对应多个用户(user )。
多对一关联映射:是在" 多" 的一端添加一个外键指向" 一" 的一端,它维护的关系为多到一的关系,如:当载入一个用户(user )时将会同时载入组(group )的信息。它的关系映射将写在多的一端(user ):
< many-to-one name="group" column = "relatedGroup" cascade = "all" /> 此时它在多的一端( user )添加了一个外键 “ relateGroup ” 指向一的一端。在多的一端通过 group 维护一的一端。
一对多关联映射:是在" 多" 的一端添加一个外键指向" 一" 的一端,它维护的关系为一到多的关系,如:当载入一个组(group )时将会同时载入此组用户(user )的信息。它的关系映射将写在一的一端(group ):
< set name="users" order-by = "name" >
< key column = "relatedGroup" />
< one-to-many class = "com.dvnchina.hibernate.User" />
</ set >
此时通过 < key column = "relatedGroup" /> 在多的一端( user )添加了一个外键 “ relateGroup ” 指向一的一端。在一的一端通过 users 维护多的一端。
总之,一对多和多对一的映射策略是一致的,都是通过在" 多" 的一端添加一个外键指向" 一" 的一端,只是站的角度不同。
多对多就是2个 一对多的叠加设置
*********************************************************************
一,
HQL 语言基本用法
1.实体查询
String hql = " from TUser";
执行这条语句会返回TUser以及TUser子类的纪录。
hql = "from java.lang.Object"
会返回数据库中所有库表的纪录。
where 语句
hql = "from TUser as user where user.name='yyy'";
其中,as可以省略也一样
hql = "from TUser user where user.name='yyy'";
where子句中,我们可以通过比较运算符设定条件,如:
=, <>, >, <, >=, <=, between, not between, in, not in, is, like等。
2.属性查询
List list = session.createQuery("select user.name, user.age from TUser as user").list();
还可以在HQL中动态构造对象实例的方法,将数据封装。
List list = session.createQuery("select new TUser(user.name, user.age) from TUser as user").list();
Iterator it = list.iterator();
while(it.hasNext() ) {
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
但是要注意这里的TUser对象只是对name和age属性的封装,其他状态均未赋值,所以不能用它来进行更新操作。
也可以在HQL的Select子句中使用统计函数
"select count(*) ,min(user.age) from TUser as user"
也可以使用distinct关键字来删除重复纪录。
select distinct user.name from TUser as user;
3.实体的更新与删除
hibernate 2中需要先查询出实体,设置属性后再保存。
hibernate 3中,提供了更灵活的方式(bulk delete/update)
更新:
Query query = session.createQuery("update TUser set age=18 where id=1");
query.executeUpdate();
删除:
session.createQuery("delete TUser where age>=18");
query.executeUpdate();
4.分组与排序
Order by子句:
from TUser user order by user.name, user.age desc
Group by子句和Having子句
"select count(user), user.age from TUser user group by user.age having count(user)>10"
5.参数邦定
通过顺序占位符?来填充参数:
1)hibernate 2 中通过session.find方法来填充
session.find("from TUser user where user.name=?", "Erica", Hibernate.STRING);
多个参数的情况:
Object[] args = new Object[] {"Erica", new Integer(20)};
Type[] types = new Type{Hibernate.STRING, Hibernate.INTEGER};
session.find("from TUser user where user.name=? and user.age=?", args, types);
2)通过Query接口进行参数填充:
Query query = session.createQuery("from TUser user where user.name=? and user.age>?");
query.setString(0,"Erica");
query.setInteger(1, 20);
通过引用占位符来填充参数:
String hql = "from TUser where name=:name";
Query query = session.createQuery(hql);
query.setParameter("name","Erica");
query.setString("name","Erica");//?区别
甚至可以将查询条件封装为一个JavaBean
class UserQuery {
private String name;
private Integer age;
//getter and setter
}
String hql = "from TUser where name=:name and age=:age";
Query query = session.createQuery(hql);
UserQuery uq = new UserQuery();
uq.setName("Erica");
uq.setAge(new Integer(20));
query.setProperties(uq); //会调用里面的getter?
query.iterate();
6.联合查询
也可以使用 inner join,left outer join, right out join, full join
排列组合:form TUser, TAddress
************************************************************
二,
1. 一.对象关系映射基础
2. 1.hibernate对象属性映射
3. 映射文件中,<property>元素的access属性用于指定Hibernate访问持久化类的属性的方式。有以下两种可选值:
4. property:这是默认值,表示是通过属性相应的get,set方法来访问属性。
5. field:表面是运用Java反射机制直接访问类的属性,此属性可以没有get,set方法。
6. 例如:<property name="name" access="field"/>
7. 这样设置是直接通过属性值取得,可以没有get,set方法。
8. 反之name则是根据set,get后的Name对应,配置文件的name名可以不跟实体bean里的属性名直接对应。
9. 我们常用的hql语句例如:List customers = session.find("from Customer as c where c.name = 'Tom'");
10. 这里的c.name中的name名字是根据配置文件里的name所对应,并不是和实体bean里的属性名对应。
11.
12. 2.在持久化类的访问方法中加入程序逻辑
13. (1)hibernate在执行find方法时候会调用bean中的set方法把查询出的内容放入属性中,提供给我们获取其中的值。
14. 在执行save,update等方法时候会调用bean中的get方法获得我们提交的数据进行持久化操作。
15. 所以,可以通过在set,get方法中加入相应我们需要的逻辑,如果不需要让hibernate在执行查询或保存操作时候隐式的调用get,set方法,
16. 可以在映射文件中设置field参数
17. (2)利用<property>元素的formula属性。
18. 如果数据表里没有totalPrice价格总和字段,而我们需要在获得用户对象的同时,需要知道它对应多个订单的价格总和,
19. 我们可以在CUSTOMER实体bean里定义totalPrice属性,生成get,set方法,再利用以下代码可以实现
20. 例如:<property name="totalPrice" formula="(select sum(o.PRICE) rrom ORDERS o where o.CUSTOMER_ID=ID)"/>
21. (3)控制insert和update语句
22. 例如:<property name="price" update="false" insert="false" column="PRICE"/>
23. 设置成false代表在保存或修改此类时候,price不被操作。一般多用于多对一关系映射中,避免级联操作。
1. 二.关联映射
2. 1.主键自动生成映射
3. <id name="id" type="java.lang.String" column="ID" length="32">
4. <generator class="uuid" />
5. </id>
6. 2.一对一主键关联
7. <one-to-one name="order" class="mypack.Order" cascade="all"/>
8. constrained属性为true,表明ORDERS表的ID主键同时作为外键参照CUSTOMERS表。
9. 当前xml文件中,必须为OID使用foreign标识符生成策略:
10. <one-to-one name="customer" class="mypack.Customer" constrained="true"/>
11. <id name="id" type="java.lang.String" column="ID">
12. <generator class="foreign">
13. <param name="property">customer</param>
14. </generator>
15. </id>
16. 注:当两个关联类之间有两个一对一关联时,可以使用一对一外键关联。
17. 一对一外键关联与多对一关联的区别仅仅是在many-to-one标签上需要配置属性unique="true"。
18. 3.多对一关联
19. <many-to-one name="customer" class="mypack.Customer" column="CUSTOMER_ID"/>
20. 4.一对多关联
21. <set name="orders" cascade="save-update" inverse="true" lazy="extra">
22. <key column="CUSTOMER_ID" />
23. <one-to-many class="mypack.Order"/>
24. </set>
25. <list name="orders" cascade="save-update" lazy="extra" fetch="join">
26. <key column="CUSTOMER_ID"></key>
27. <list-index column="SHOW_INDEX"></list-index> <!--orders表中索引位置字段SHOW_INDEX-->
28. <one-to-many class="mypack.Order" />
29. </list>
30. 5.多对多关联
31. <set name="orders" table="CUSTOMERS_TO_ORDERS" cascade="save-update" lazy="extra">
32. <key column="CUSTOMER_ID"/> <!--双向多对多关联必须把其中一端的inverse属性设为true-->
33. <many-to-many class="mypack.Order" column="ORDER_ID"/>
34. </set>
35. 注:当中间表需要有更多的属性字段时,可把多对多分解成两个一对多关联。
36. 6.在数据库中对集合排序
37. set,map映射支持sort属性(内存排序),order-by属性(数据库排序)。
38. 例如:
39. <set name="orders" cascade="save-update" inverse="true" lazy="extra" order-by="ID asc">
40. <key column="CUSTOMER_ID" /> <!--当加载Customer对象的orders集合的时,会进行排序操作-->
41. <one-to-many class="mypack.Order"/>
42. </set>
43. 7.附加过滤条件
44. <set name="orders" cascade="save-update" inverse="true" lazy="extra" where="STATUS='1'">
45. <key column="CUSTOMER_ID" /> <!--当加载Customer对象的orders集合的时,只会查出STATUS为1的order对象-->
46. <one-to-many class="mypack.Order"/>
47. </set>
1. 三.检索方式
2. 1.分页查询
3. List result = this.getSession().createQuery("from Customer c order by c.name asc")
4. .setFirstResult(0).setMaxResults(10).list();
5. 2.条件查询
6. //如果hql语句中含有参数可以使用以下方法提高安全性:
7. Object[] args = {name,order}; //传入的查询条件的变量名字
8. Type[] types = {Hibernate.STRING,Hibernate.entity(Order.class)}; //变量类型
9. //Type[]是hibernate提供的变量类型,order为自定义对象类型。
10. //根据参数说在位置的索引值,传入的变量名字,变量类型来进行查询。
11. List list = this.query("from Customer c where c.name=? and c.order=?",args,types)
12. public List query(String hql, Object[] args, Type[] types){
13. Query query = this.getSession().createQuery(hql);
14. query.setParameters(args, types);
15. return query.list();
16. }
17. 3.批量延迟检索
18. 映射文件中<set>元素有一个batch-size属性,用于为延迟检索或立即检索策略设定批量检索的数量。
19. 注:批量检索的属性值范围不宜过大,如果过大就失去了延迟加载的意义,如果太小也会失去批量检索的意义。
20. 一般设置为:3-10。合理的运用批量检索可以提高检索性能。具体请查阅说明文档。
21. 4.检索单个对象
22. Customer customer = (Customer)this.getSession().createQuery("from Customer c order by c.name asc")
23. .setMaxResults(1).uniqueResult();
24. 5.隐式内连接
25. //标准的HQL内连接查询语句:
26. "from Customer c inner join c.orders";
27. //如果Customer类中没有orders集合属性,可以采用SQL风格的隐式内连接查询语句:
28. "from Customer c,Order o where c.in=o.customer_id";
29. 6.分组查询
30. //例如以下查询语句仅统计具有一条以上订单的客户的所有订单的总价:
31. "select c.id,c.name,sum(o.price) from Customer c join c.orders o group by c.id having (count(o)>1)";
32. 7.HQL查询的select子句
33. "select new map(c.name as personName) from Customer c"
34. //HQL语句返回的结果是集合,其中集合元素是Map对象,以personName作为Map的key。
************************************************************
三,
一级缓存
实验:
1.在同一个session中2次load
load会从缓存中读取数据
2.在同一个session中2次get
get会从缓存中中读取数据
3.在同一个session中2次迭代 .iterate().next();
hql势必会执行 但是迭代会从缓存中读取数据 第一次迭代出现n+1问题(会发出查询id的sql 不会发现查询实体的sql)
4.在同一个session中2次查询属性
缓存只缓存实体对象
5.在不同的session中发出load查询
一级缓存和session绑定
6.在同一session中先save 后load
save也是会将实体放入session中
7.在同一session中先load, 再session.clear()或session.evict(),最后再load()
总结:
支持一级缓存的方法:
get load iterate
管理一级缓存
clear evict
二级缓存
也成为SessionFactory级缓存,二级缓存可以被所有Session共享
SessionFactory可以管理二级缓存 二级缓存与SessionFactory的生命周期一致
将echcache.xml 拷贝至src下
<property name="hibernate.cache.user_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<class-cache class="com.illu.pojo.Xxx" usage="read-only"/>
readonly策略 + idle失效 保证数据库与内存中的一致性
************************************************************
四,
二级缓存配置:
http://blog.163.com/chenhongbin007/blog/static/3406992120094661950804/
1、首先要打开二级缓存,在hibernate.cfg.xml中添加如下配置:
<property name="hibernate.cache.use_second_level_cache">true</property>
2、Hibernate的二级缓存使用第三方的缓存工具来实现,所以我们需要指定Hibernate使用哪个
缓存工具。如下配置指定Hibernate使用EhCache缓存工具。
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3、Hibernate在默认情况下并不会对所有实体对象进行缓存,所以,我们需要指定缓存哪些对象,
在实体对象的映射文件中(相应的<class>标签内部),添加如下配置:
<cache usage="read-only"/>
usage="read-only"是“只读”缓存策略。
注意,这个<cache>标签只能放在<class>标签的内部,而且必须处在<id>标签的前面!!!
这个<cache>标签放在哪些<class>标签下面,就说明会对这些类的对象进行缓存
4、对于第3步,有一个可选的方案是在hibernate.cfg.xml文件中指定哪些类的对象需要缓存,
而不需要使用<cache>标签来指定。如:
在hibernate.cfg.xml中添加如下配置:
<class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" />
注意,这个<class-cache>标签必须放在<mapping>标签的后面!!
1、首先设置EhCache,建立配置文件ehcache.xml,默认的位置在class-path,可以放到你的src目录下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000" <!-- 缓存最大数目 -->
eternal="false" <!-- 缓存是否持久 -->
overflowToDisk="true" <!-- 是否保存到磁盘,当系统当机时-->
timeToIdleSeconds="300" <!-- 当缓存闲置n秒后销毁 -->
timeToLiveSeconds="180" <!-- 当缓存存活n秒后销毁-->
diskPersistent="false"
diskExpiryThreadIntervalSeconds= "120"/>
</ehcache>
2、在Hibernate配置文件中设置:
<!-- 设置Hibernate的缓存接口类,这个类在Hibernate包中 -->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 是否使用查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
如果使用spring调用Hibernate的sessionFactory的话,这样设置:
<!--HibernateSession工厂管理 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="datasource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
</props>
</property>
<property name="mappingDirectoryLocations">
<list>
<value>/WEB-INF/classes/cn/rmic/manager/hibernate/</value>
</list>
</property>
</bean>
说明一下:如果不设置“查询缓存”,那么hibernate只会缓存使用load()方法获得的单个持久化对象,如果想缓存使用 findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集的话,就需要设置
hibernate.cache.use_query_cache true 才行
3、在Hbm文件中添加<cache usage="read-only"/>
4、如果需要“查询缓存”,还需要在使用Query或Criteria()时设置其setCacheable(true);属性
5、实践出真知,给一段测试程序,如果成功的话第二次查询时不会读取数据库
package cn.rmic.hibernatesample;
import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import cn.rmic.hibernatesample.hibernate.HibernateSessionFactory;
import cn.rmic.manager.po.Resources;
public class testCacheSelectList ...{
/** *//**
* @param args
*/
public static void main(String[] args) ...{
// TODO Auto-generated method stub
Session s=HibernateSessionFactory.getSession();
Criteria c=s.createCriteria(Resources.class);
c.setCacheable(true);
List l=c.list();
// Query q=s.createQuery("From Resources r")
// .setCacheable(true)
// .setCacheRegion("frontpages") ;
// List l=q.list();
Resources resources=(Resources)l.get(0);
System.out.println("-1-"+resources.getName());
HibernateSessionFactory.closeSession();
try ...{
Thread.sleep(5000);
} catch (InterruptedException e) ...{
// TODO Auto-generated catch block
e.printStackTrace();
}
s=HibernateSessionFactory.getSession();
c=s.createCriteria(Resources.class);
c.setCacheable(true);
l=c.list();
// q=s.createQuery("From Resources r").setCacheable(true)
// .setCacheRegion("frontpages");
// l=q.list();
resources=(Resources)l.get(0);
System.out.println("-2-"+resources.getName());
HibernateSessionFactory.closeSession();
}
}
************************************************************
五
1:1共享主键关系
对象模型代码
public class Person implements java.io.Serializable {
private Long id;
private String name;
private Address address;
public class Address implements java.io.Serializable {
private Long id;
private Person person;
private String detail;
四、映射代码
<? xml version ="1.0" encoding ="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name ="entity.Person" table ="person" >
< id name ="id" type ="java.lang.Long" >
< column name ="id" />
< generator class ="identity" />
</ id >
< property name ="name" type ="java.lang.String" >
< column name ="name" length ="24" not-null ="true" >
< comment > 姓名</ comment >
</ column >
</ property >
<!-- cascade="all":在保存person对象的时候,级联保存person对象关联的address对象 -->
< one-to-one name ="address" cascade ="all" />
</ class >
</ hibernate-mapping >
<? xml version ="1.0" encoding ="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name ="entity.Address" table ="address" catalog ="mydb" >
< id name ="id" type ="java.lang.Long" >
< column name ="id" />
<!-- class="foreign": 一对一主键映射中,使用另外一个相关联的对象的标识符 -->
< generator class ="foreign" >
< param name ="property" > person</ param >
</ generator >
</ id >
< property name ="detail" type ="java.lang.String" >
< column name ="detail" length ="120" not-null ="true" >
< comment > 详细地址</ comment >
</ column >
</ property >
<!-- 表示在address表存在一个外键约束,外键参考相关联的表person -->
< one-to-one name ="person" constrained ="true" />
</ class >
</ hibernate-mapping >
五、Hibernate配置
<? 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">
<!-- Generated by MyEclipse Hibernate Tools. -->
< hibernate-configuration >
< session-factory >
< property name ="connection.username" > root</ property >
< property name ="connection.url" >
jdbc:mysql://localhost:3306/mydb
</ property >
< property name ="dialect" >
org.hibernate.dialect.MySQLDialect
</ property >
< property name ="connection.password" > xiaohui</ property >
< property name ="connection.driver_class" >
com.mysql.jdbc.Driver
</ property >
< property name ="show_sql" > true</ property >
< property name ="format_sql" > true</ property >
< mapping resource ="entity/Person.hbm.xml" />
< mapping resource ="entity/Address.hbm.xml" />
</ session-factory >
</ hibernate-configuration >
*********************************************************************************************************
session的方法
get方法:
返回的对象是persistent对象
调用时发出查询sql
查不到返回null
实例:
User user = (User)session.get(User.class, 1);
第二个参数为主键,如果是主键生成策略是uuid,则应传一个字符串,上面是native生成的,所以用传的是int
load方法
返回的对象是persistent对象
调用延迟加载,原理是用CGLIB生成代理类,采用继承,生成子类
真正使用对象的时候才发出查询sql语句
如果查找不到会产生异常
实例:
User user = (User)session.load(User.class, 1);
delete方法
使用Session.delete()会把对象的状态从数据库中移除。 当然,你的应用程序可能仍然持有一个指向已删除对象的引用。所以,最好这样理解:delete()的用途是把一个持久实例变成瞬时(transient)实例。
实例:
session.delete(user);
Query简单使用
//创建query,参数为HQL语句
Query query = session.createQuery("from User");
//分页
query.setFirstResult(0);
query.setMaxResults(2);
//获取选出的记录
List list = query.list();
以下是Hibernate文档里对对象三种状态的描述
瞬时(Transient) - 由new操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识 (identifier)。 如果瞬时(Transient)对象在程序中没有被引用,它会被垃圾回收器(garbage collector)销毁。使用Hibernate Session可以将其变为持久(Persistent)状态。(Hibernate会自动执行必要的SQL语句)
持久(Persistent) - 持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行UPDATE。将对象从持久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行DELETE语句。
脱管(Detached) - 与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。 对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的Session上,会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)。这个功能使得一种编程模型,即中间会给用户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可能。 我们称之为应用程序事务,即从用户观点看是一个操作单元(unit of work)。
三种状态的特征:
transient状态的特征
在数据库中没有与之匹配的数据
没有纳入session的管理
persistent状态的特征
在数据库中有与之匹配的数据
纳入了session的管理
在清理缓存(脏数据检查)的时候,自动与数据库同步
detached状态的特征
在数据库中有与之对应的数据
没有纳入session的管理
可以手动构造detached状态的对象
把ID设置为数据库中存在的即可
***************************************
悲观锁
悲观锁的实现,通常依赖于数据库机制,在整个过程中都将数据锁定,其他任何用户都不能读取或者修改,因此并发性不好,适合于短事务。使用悲观锁会是Lazy失效。
session.get/load(class,id,LockMode.UPGRADE);
===========================
乐观锁
乐观锁不是锁,是冲突检测机制,一个用户改的时候,别人可以随便改。
版本号:
假设初始版本号为0,有用户改一次,版本号+1
大多数是基于数据库版本记录机制,一般是在数据库表中加入一个version字段。
读取数据时将版本号一同读出,之后更新数据时,版本号加一,如果提交数据时,版本号小于或等于数据库表中的版本号,则认为数据是过期的,不能更新,出现更新丢失(Lost Update)。否则给予更新。
旧数据不能更新新数据,如果当前版本低于数据库中的版本,则不能进行更新。即一个用户拿到数据之后,在更改之前已经被别的用户更改过,则此时不能对其更新。
配置
在实体中加一个version(int),由Hibernate进行维护:
class标签的optimistic-lock="version",这是默认配置,可不显示声明
映射文件中配置属性(位置在普通属性配置之前):
<version name="version"/>
加载时不需要设置锁模式
缺点:
数据量比较多,因为别人的更改而不能更新,大量的数据输入无效。
*************************************************
Hibernate关系映射的说明
Hibernate开源框架对数据层的映射包括实体的映射和关系的映射,其中关系的映射是最复杂的。如果你掌握不好关系映射,你干脆就不要用,否则会严重地影响性能。
Hibernate中的实体关系分为四种,基本上与我们在数据库建模中了解到的实体关系是一样的,即一对一关系、一对多关系、多对一关系、多对多关系。我们下面分别给出说明:
一、一对一关系、多对一关系
一对一、多对一关系在代码上的体现是在JavaBean中包含一个实体类属性。比如,夫妻关系是一对一的关系,那么丈夫的属性中就应该有一个属性是妻子,妻子的属性中也应该有一个属性是丈夫。同样,多对一的关系中,在代码上的体现也是在Java中包含一个实体类属性。比如,孩子与妈妈的关系就是多对一的关系,每一个孩子都应该有一个属性是妈妈。我们发现,无论是一对一,还是多对一,它们在代码是都是一样的,就是属性中包含一个实体类属性。而事实上,一对一关系是多对一关系的一种特例而已。所以,在映射时,由外键实现的一对一关系或多对一关系时,无论是哪一种,外键所在一方关系属性都是通过many- to-one映射的,而不是用one-to-one。
二、一对多关系、多对多关系
这两种关系也有些共性,就是它们在代码上的体现都是在JavaBean中包含一个集合属性。比如在上面说的妈妈与孩子的关系,妈妈应该包含一个集体属性,在这个集合中包含了她所有的小孩。这种一对多关系使用one-to-many来映射,在数据库中的体现是在一的一方是不含有外键的。而多对多的关系虽然也是在属性中包含一个集合属性,但在映射时使用的却是many-to-many。这主要是因为多对多关系需要一个关系表,必须告诉Hibernate这个关系表是谁。
以上只是简单的概括了一下Hibernate中一些关系映射的特点,下面来说说Hibernate关系映射中的难点问题。
如果不是真正使用Hibernate开发过项目,恐怕很难理解为什么说如果掌握不好关系最好不要使用关系。这里面有这样几个问题:
1、关系的级联操作问题。
我们知道,在数据库中,建立外键的同时还要建立约束关系。这就存在一个问题,即当外键所指向表的行被删除时,当前行应该如何操作。比如,丈夫表含有一个外键指向妻子,如果妻子被删除了,那么丈夫所在行的外键的值应该如何操作?如果保持原有值不变,但他所指向的妻子却已经不在了,这样的记录就没有意义了,所以必须要采取一些行为。在数据库中一般会根据用户的设置采取三种行为,一是给出提示,告诉你因为有外键指向这个行,所以不能删,如果非要删除,必须先将指向它的外键却除;二是在删除当前行后,自动将指向它的外键置为空;三是删除当前行后,将所有指向它的行也同时删除。
一般数据库默认情况下都采取第一种行为,这样如果不做任何设置,当我们对一个实体进行删除时,就会报错,告诉你有外键指向不能删除。
这种问题如何解决呢?你可以事先做好数据库设计,让数据库帮你采取一种更合适的行为。两种可选的确定行为,即置空或级联删除。究竟采用哪一种更合适,要视你的应用而定,限于篇幅,我这里就不做过多的讲解了。再者,可以使用Hibernate的cascade属性,它的delete代表删除时置空外键,而 delete-orphan则代表删除时同时删除指向它的行。
2、关系的方向性问题
“一个巴掌拍不响”,一个关系也一定有两个实体。这就存在了另外一个问题,当一个实体发生变化时,关系是不是也一定要跟着变化?这种问题很难说清,因为它跟具体的应用关联。有时,我们只要更新实体,而不想更新关系;而有时我们又想更新关系而不更新实体;还有些情况下,我们是实体和关系同时都要更新。在默认情况下,Hibernate对于实体和关系是同时更新的,即使你根本没有更改过关系。这对于性能的影响比较大。我们可以给关系设置一个inverse属性,告诉它在任何变化下,都不要更新关系。当然还有其它的办法,读者可以参考其文档。总之,这是一个非常复杂的问题,要视你的应用而定。
3、N+1查询问题
关于什么是 N+1查询,我不想解释,读者可以看一下我前面的文章或者到网上去查询。总的来说N+1查询的问题就是性能太低,在有些情况下甚至会导致系统崩溃。但有些时候它又是有益的。因为N+1查询实际上是延迟加载了,它节省了空间。Hibernate有一个fetch属性,用于说明抓取数据的策略,如果选择了 join则不会使用N+1查询,但加载上来了所有的数据并不一定都是你想要的,也可能会浪费存储空间。
4、延迟加载
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载。那么Hibernate是怎么知识用户在什么时候使用数据了呢?又是如何加载数据呢?其实很简单,它使用了代理机制。返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用getter方法时就会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关闭了。这种情况就叫做未初始化的关系。
延迟加载的好处就是节省了存储空间。因为我们并不是在所有情况下都需要关系数据。比如,妈妈和孩子。如果你只想修改妈妈的数据,而Hibernate将她10几个孩子也同时给你加载进来了,这显然是无意义的。所以你可以使用Hibernate.initialize()方法主动地去决定是否初始化关系。当然也可以在配置文件中通过lazy属性,但这样一来就固定了,要么延迟,要么不延迟。
Hibernate的关系还有很多问题,这里限于篇幅先讲这么多。还是开头的那句话,如果你没有掌握好Hibernate中关系的映射,那你干脆就不要用了,否则严重地影响性能。
************************************************************,