??xml version="1.0" encoding="utf-8" standalone="yes"?>
Hibernate会尽量将与数据库的操作gq,直到必须要与数据库进行交互,例如saveҎ一般会在提交时才真正执行,最l在提交时会以批处理的方式与数据库进行交互,以提高效率?/p>
而将操作延迟Q就是利用缓存,最后要处理的操作放到缓存中?/p>
flushҎ的主要作用就是清理缓存,强制数据库与Hibernate~存同步Q以保证数据的一致性。它的主要动作就是向数据库发送一pd的sql语句Qƈ执行q些sql语句Q但是不会向数据库提交。而commitҎ则会首先调用flushҎQ然后提交事务?br />
在下面的情况下,Hibernate会调用Session.flush()以清理缓存:
1)事务提交Ӟ如果flush模式不ؓFlushMode.NEVER,commit()调用flush().
2)在某些查询语句之前(此查询语句之前的语句已经改变了数据库状态,所以需要调用flushQ)以同步数据库是查出来的数据是l过更改的)?br />
除非明确地指定了flushQ)命oQ否则关于Session何时会执行这些JDBC调用完全是无法保证的Q只能保证他们执行的前后序?/p>
通过讄session.setFlushMode(),可以_控制Hibernate的FlushMode.
(1) FlushMode.AUTO:Hibernate判断对象属性有没有改变Q如果被更改成ؓ脏数据,则在一个查询语句前更新此改动以保证数据库的同步。这也是Hibernate的默认清理模式?
(2) FlushMode.COMMIT:在事务结束之前清理session的缓存。这h可能D查出脏数?
(3) FlushMode.NEVERQ除非强制调用Session.flush(),否则永远不清理Session。相当于数据库讄Z个只ȝ数据库?
【如果此时进行数据的写入操作Q会发生错误?br />
(4) FlushMode.ALWAYSQ在每一个查询数据之前都调用Session.flush()。很昄q种效率很低?
在调用Session.flush()Ӟ涉及的SQL语句会按照下面的序执行?
Q?Q?nbsp; 所有的实体q行插入的语句,光序按照对象执行Session.save()的时间顺序?
Q?Q?nbsp; 所有对实体q行更新的语?
Q?Q?nbsp; 所有对集合元素q行删除Q更新或者插入的语句
Q?Q?nbsp; 所有对实体l行删除的语句,光序按照对象执行Session.delete()的时间顺序?
Q?Q?nbsp; 有一个例外是Q如果对象用native方式生成的IDQ持久化标识Q,则他们一执行save׃被插入?
【因为native方式若想得到主键Q必M数据库交互,而hilo方式则不用】)
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/MageShuai/archive/2009/09/11/4544202.aspx
二)dynamic statements generated at runtime
1)<property>元素 dynamic-insert属?讄为true,表示insert对象的时?生成动态的insert语句,如果q个字段的值是null׃会加入到insert语句当中.默认false
2)<property>元素 dynamic-update属?讄为true,表示update对象的时?生成动态的update语句,如果q个字段的值是null׃会被加入到update语句?默认false
3)<class>元素 dynamic-insert属?讄为true,表示把所有的<property>元素的dynamic-insert属性设|ؓtrue,默认false
4)<class>元素 dynamic-update属?讄为true,表示把所有的<property>元素的dynamic-update属性设|ؓtrue,默认false
ȝQ如果表中包含N多字D?把dynamic-update属性和insert属性设|ؓtrue,q样在插入和修改数据的时?语句中只包括要插入或者修改的字段.可以节省SQL语句的执行时?提高E序的运行效?(In some situations, such as a legacy table with hundreds of columns where the SQL statements will be large for even the simplest operations (say, only one column needs updating), you have to turn off this startup SQL generation and switch to dynamic statements generated at runtime.)
其他属性设|对SQL Statement影响
1)<property>元素 insert属?讄为false,在insert语句中不包含q个字段,表示永远不会被插?默认true
2)<property>元素 update属?讄为false,在update语句中不包含q个字段,表示永远不会被修?默认true
3)<class>元素 mutable属?讄为false是把所有的<property>元素的update属性设|ؓ了false,说明q个对象不会被更?默认true
Ҏ | 說明 |
Restrictions.eq | {於 |
Restrictions.allEq | 使用MapQ用key/value進行多個等於的比對 |
Restrictions.gt | 大於 > |
Restrictions.ge | 大於{於 >= |
Restrictions.lt | 於 < |
Restrictions.le | 於{於 <= |
Restrictions.between | 應SQL的BETWEEN子句 |
Restrictions.like | 應SQL的LIKE子句 |
Restrictions.in | 應SQL的in子句 |
Restrictions.and | and關係 |
Restrictions.or | or關係 |
Restrictions.sqlRestriction | SQL限定查詢 |
==============
前面讲了Criteria看v来比HQL眼多了Q接着l箋?br />
如果每个女都有自己的客戯源(不要x了!Q,那么需要查询拥有客户Gates的美x么办?
使用Criteria可以有两U方法:
1Q?br />
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createCriteria("customers");
beautyCriteria.add(Restrictions.eq("name", "Gates")):
2Q?br />
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createAlias("customers", "c");
beautyCriteria.add(Restrictions.eq("c.name", "Gates")):
接着有了新的要求Q年U太大的女不要Q还是查找拥有客户Gates的,条g如下Q?br />
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class, "b").;
DetachedCriteria customerCriteria = beautyCriteria.createAlias("customers", c");
beautyCriteria.add(Restrictions.le("b.age", new Long(20))):
customerCriteria.add(Restrictions.eq("c.name", "Gates")):
关于Criteria更详l的资料QHibernate的源代码和测试是最好的文?br />
Criteria的缺点?DBA很生气,后果很严重?/p>
Transient instances may be made persistent by calling save(),persist() or saveOrUpdate(). Persistent instances may be made transient by calling delete(). Any instance returned by a get() or load() method is persistent. Detached instances may be made persistent by calling update(), saveOrUpdate(), lock() or replicate(). The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge().save() and persist() result in an SQL INSERT, delete() in an SQL DELETE and update() or merge() in an SQL UPDATE. Changes to persistent instances are detected at flush time and also result in an SQL UPDATE. saveOrUpdate() and replicate() result in either an INSERT or an UPDATE.
persist():
Make a transient instance persistent. This operation cascades to associated instances if the association is mapped with cascade="persist". The semantics of this method are defined by JSR-220.
当联风gؓpersistӞq个操作会联操作关联的对象。这个方法的语义是JSR-220定义的?
persist() is well defined. It makes a transient instance persistent. However,
it doesn't guarantee that the identifier value will be assigned to the persistent
instance immediately, the assignment might happen at flush time. The spec doesn't say
that, which is the problem I have with persist().
persist() also guarantees that it will not execute an INSERT statement if it is
called outside of transaction boundaries. This is useful in long-running conversations
with an extended Session/persistence context.A method like persist() is required.
save() does not guarantee the same, it returns an identifier, and if an INSERT
has to be executed to get the identifier (e.g. "identity" generator, not "sequence"),
this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is not good in a long-running conversation with an extended Session/persistence context."
save():
Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.) This operation cascades to associated instances if the association is mapped with cascade="save-update".
首先会赋予一个标志符Q然后持久化l定的对象。但是如果用assigned 标志W生方法,则会使用当前的标志符倹{如果联风gؓsaveQpdateQ则会持久化相应的关联对?
merge():
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge". The semantics of this method are defined by JSR-220.
拯当前的对象的状态到相同标志W的持久化对象的状态。如果当前关联的session中没有持久化对象QHibernate会加载该对象Qƈq回q个持久化对象。如果给定的对象没有保存Q则Hibernate保存该副本,q回q个新持久化的对象,但给定的对象q没有和session兌。如果指定联风gؓmergeQ则会联关联对象。该Ҏ的语义是JSRQ?00定义的?
saveOrUpdate():
Either {@link #save(String, Object)} or {@link #update(String, Object)} the given instance, depending upon resolution of the unsaved-value checks (see the manual for discussion of unsaved-value checking). This operation cascades to associated instances if the association is mapped with cascade="save-update".
保存或更新给定对象,取决于给定对象的持久化状态。默认情况下是保存,在指定标志符元素属性ؓunsaved-value时则会调整。如果联风gؓ"save-update",则会U联相关联的对象
delete():
Remove a persistent instance from the datastore. The object argument may be an instance associated with the receiving Session or a transient instance with an identifier associated with existing persistent state. This operation cascades to associated instances if the association is mapped with cascade="delete".
删除对象?
lock():
Obtain the specified lock level upon the given object. This may be used to perform a version check (LockMode.READ), to upgrade to a pessimistic lock (LockMode.UPGRADE), or to simply reassociate a transient instance with a session (LockMode.NONE). This operation cascades to associated instances if the association is mapped with cascade="lock".
用于Ҏ定对象设|参Cl定的锁。如果锁为LockMode.READQ可以用来执行版本检查;如果锁ؓLockMode.UPGRADE则用来执行悲观锁。如果定为LockMode.NONEQ则仅仅时持久化一个未持久化的对象。如果联风gؓlockQ会U联相关联的对象?
refresh():
Re-read the state of the given instance from the underlying database. It is inadvisable to use this to implement long-running sessions that span many business tasks. This method is, however, useful in certain special circumstances.For example where a database trigger alters the object state upon insert or update after executing direct SQL (eg. a mass update) in the same session after inserting a Blob or Clob.
从当前数据库中重新读取给定对象的状态。这在执行跨多个业务Q务的长时间session中优势不显著Q在数特定环境下还是有用的Q例如: 插入或更新后数据库触发器更改了对象的状态。当前session中执行完原生SQL后(如批量更斎ͼ?插入Blob或Clob对象后?br />
evict():
Remove this instance from the session cache. Changes to the instance will not be synchronized with the database. This operation cascades to associated instances if the association is mapped with cascade="evict".
从session的缓存中去除当前实例。执行后对象的改变将不再和数据库保持同步。当指定U联风格?evict’Ӟ会联操作关联对象。我用的时候主要是用于扚w操作的时候,清空~存Q防止内存紧张?
replicate():
Persist the state of the given detached instance, reusing the current identifier value. This operation cascades to associated instances if the association is mapped with cascade="replicate"
重用当前的标志符Q持久化当前已经qsession的对象的状态。具体的操作和要看参数ReplicationMode。如果是ReplicationMode.EXCEPTION,则在复制时如果有重复的行数据Q则抛出异常。ReplicationMode.IGNORE则忽略异常。ReplicationMode.OVERWRITE则会覆盖掉已有行数据。ReplicationMode.LATEST_VERSION则是在有重复时用最新的版本q行控制?
Hibernate的用h要求一个既可自动分配新持久化标?identifier)保存瞬时(transient)对象Q又可更?重新兌q(detached)实例的通用Ҏ?saveOrUpdate()Ҏ实现了这个功能?nbsp;saveOrUpdate()用途和语义可能会新用h到迷惑?首先Q只要你没有试在某个session中用来自另一session的实例,你就应该不需要用update()Q?saveOrUpdate()Q或merge()。有些程序从来不用些Ҏ?
通常下面的场景会使用update()或saveOrUpdate()Q?
E序在第一个session中加载对?
该对象被传递到表现?
对象发生了一些改?
该对象被q回C务逻辑?
E序调用W二个session的update()Ҏ持久q些改动
saveOrUpdate()做下面的?
如果对象已经在本session中持久化了,不做M?
如果另一个与本session兌的对象拥有相同的持久化标?identifier)Q抛Z个异?
如果对象没有持久化标?identifier)属性,对其调用save()
如果对象的持久标?identifier)表明其是一个新实例化的对象Q对其调用save()
如果对象是附带版本信息的Q通过<version>?lt;timestamp>Q?q且版本属性的D明其是一个新实例化的对象Qsave()它?
否则update() q个对象
merge()可非怸?
如果session中存在相同持久化标识(identifier)的实例,用用L出的对象的状态覆盖旧有的持久实例
如果session没有相应的持久实例,则尝试从数据库中加蝲Q或创徏新的持久化实?,最后返回该持久实例
用户l出的这个对象没有被兌到session上,它依旧是q?/span>.
Session刷出(flush)
每间隔一D|_Session会执行一些必需的SQL语句来把内存中的对象的状态同步到JDBCq接中。这个过E被UCؓ刷出(flush)Q默认会在下面的旉Ҏ行:
a.在某些查询执行之?
b.在调用org.hibernate.Transaction.commit()的时?
c.在调用Session.flush()的时?
涉及的SQL语句会按照下面的序发出执行Q?
所有对实体q行插入的语句,光序按照对象执行Session.save()的时间顺?
所有对实体q行更新的语?
所有进行集合删除的语句
所有对集合元素q行删除Q更新或者插入的语句
所有进行集合插入的语句
所有对实体q行删除的语句,光序按照对象执行Session.delete()的时间顺?
Q有一个例外是Q如果对象用native方式来生成IDQ持久化标识Q的话,它们一执行save׃被插入。)
除非你明地发出了flush()指oQ关于Session何时会执行这些JDBC调用是完全无法保证的Q只能保证它们执行的前后序?当然QHibernate保证QQuery.list(..)l对不会q回已经失效的数据,也不会返回错误数据?
也可以改变默认的讄Q来让刷?flush)操作发生的不那么频繁?FlushModecd义了三种不同的方式?仅在提交时刷?仅当Hibernate的Transaction API被用时有效)Q?按照刚才说的方式刷出Q?以及除非明确使用flush()否则从不刷出?最后一U模式对于那些需要长旉保持Session为打开或者断U状态的长时间运行的工作单元很有用?
父子关系的?cascading)操作准确语义如下Q?
如果父对象被persist()Q那么所有子对象也会被persist()
如果父对象被merge()Q那么所有子对象也会被merge()
如果父对象被save()Qupdate()?saveOrUpdate()Q那么所有子对象则会被saveOrUpdate()
如果某个持久的父对象引用了瞬?transient)或者脱?detached)的子对象Q那么子对象会被saveOrUpdate()
如果父对象被删除Q那么所有子对象也会被delete()
除非被标Cؓcascade="delete-orphan"Q删?#8220;孤儿”模式Q此时不被Q何一个父对象引用的子对象会被删除Q, 否则子对象失掉父对象对其的引用时Q什么事也不会发生?如果有特D需要,应用E序可通过昑ּ调用delete()删除子对象?
大体上,对于HIBERNATE性能调优的主要考虑点如?
Ø 数据库设计调?/p>
Ø HQL优化
Ø API的正?如根据不同的业务cd选用不同的集合及查询API)
Ø 主配|参?日志Q查询缓存,fetch_size, batch_size{?
Ø 映射文g优化(ID生成{略Q二U缓存,延迟加蝲Q关联优?
Ø 一U缓存的理
Ø 针对二~存Q还有许多特有的{略
Ø 事务控制{略?/p>
1?数据库设?/strong>
a) 降低兌的复杂?/p>
b) 量不用联合主?/p>
c) ID的生成机Ӟ不同的数据库所提供的机制ƈ不完全一?/p>
d) 适当的冗余数据,不过分追求高范式
2?HQL优化
HQL如果抛开它同HIBERNATE本n一些缓存机制的兌QHQL的优化技巧同普通的SQL优化技巧一P可以很容易在|上扑ֈ一些经验之谈?/p>
3?主配|?/strong>
a) 查询~存Q同下面讲的~存不太一P它是针对HQL语句的缓存,卛_全一L语句再次执行时可以利用缓存数据。但是,查询~存在一个交易系l?数据变更频繁Q查询条件相同的机率q不?中可能会起反作用:它会白白耗费大量的系l资源但却难以派上用场?/p>
b) fetch_sizeQ同JDBC的相兛_C用类|参数q不是越大越好,而应Ҏ业务特征去设|?/p>
c) batch_size同上?/p>
d) 生pȝ中,切记要关掉SQL语句打印?/p>
4?~存
a) 数据库~存:q~存是最高效和安全的Q但不同的数据库可管理的层次q不一P比如Q在ORACLE中,可以在徏表时指定整个表|于~存当中?/p>
b) SESSION~存:在一个HIBERNATE SESSION有效Q这U缓存的可干预性不强,大多于HIBERNATE自动理Q但它提供清除缓存的ҎQ这在大扚w增加/更新操作是有效的。比如,同时增加十万条记录,按常规方式进行,很可能会发现OutofMemeroy的异常,q时可能需要手动清除这一U缓?Session.evict以及 Session.clear
c) 应用~存:在一个SESSIONFACTORY中有效,因此也是优化的重中之重,因此Q各cȝ略也考虑的较多,在将数据攑օq一U缓存之前,需要考虑一些前提条?
i. 数据不会被第三方修改(比如Q是否有另一个应用也在修改这些数?)
ii. 数据不会太大
iii. 数据不会频繁更新(否则使用CACHE可能适得其反)
iv. 数据会被频繁查询
v. 数据不是关键数据(如涉及钱Q安全等斚w的问??/p>
~存有几UŞ式,可以在映文件中配置:read-only(只读Q适用于很变更的静态数?历史数据)Qnonstrict-read- writeQread-write(比较普遍的Ş式,效率一?Qtransactional(JTA中,且支持的~存产品较少)
d) 分布式缓?同c)的配|一P只是~存产品的选用不同Q在目前的HIBERNATE中可供选择的不多,oscache, jboss cacheQ目前的大多数项目,对它们的用于集群的?特别是关键交易系l?都持保守态度。在集群环境中,只利用数据库U的~存是最安全的?/p>
5?延迟加蝲
a) 实体延迟加蝲:通过使用动态代理实?/p>
b) 集合延迟加蝲:通过实现自有的SET/LISTQHIBERNATE提供了这斚w的支?/p>
c) 属性gq加?
6?Ҏ选用
a) 完成同样一件事QHIBERNATE提供了可供选择的一些方式,但具体用什么方式,可能用性能/代码都会有媄响。显C,一ơ返回十万条记录 (List/Set/Bag/Map{?q行处理Q很可能D内存不够的问题,而如果用Z游标(ScrollableResults)?Iterator的结果集Q则不存在这L问题?/p>
b) Session的load/getҎQ前者会使用二~存Q而后者则不用?/p>
c) Query和list/iteratorQ如果去仔细研究一下它们,你可能会发现很多有意思的情况Q二者主要区?如果使用了SpringQ在HibernateTemplate中对应find,iteratorҎ):
i. list只能利用查询~存(但在交易pȝ中查询缓存作用不?Q无法利用二U缓存中的单个实体,但list查出的对象会写入二~存Q但它一般只生成较少的执行SQL语句Q很多情况就是一?无关??/p>
ii. iterator则可以利用二U缓存,对于一条查询语句,它会先从数据库中扑և所有符合条件的记录的IDQ再通过IDȝ存找Q对于缓存中没有的记录,再构造语句从数据库中查出Q因此很Ҏ知道Q如果缓存中没有MW合条g的记录,使用iterator会生N+1条SQL语句(N为符合条件的记录?
iii. 通过iteratorQ配合缓存管理APIQ在量数据查询中可以很好的解决内存问题Q如:
while(it.hasNext()){
YouObject object = (YouObject)it.next();
session.evict(youObject);
sessionFactory.evice(YouObject.class, youObject.getId());
}
如果用listҎQ很可能出OutofMemory错误了?/p>
iv. 通过上面的说明,我想你应该知道如何去使用q两个方法了?/p>
7?集合的选用
在HIBERNATE 3.1文档?#8220;19.5. Understanding Collection performance”中有详细的说明?/p>
8?事务控制
事务斚wҎ能有媄响的主要包括:事务方式的选用Q事务隔ȝ别以及锁的选用
a) 事务方式选用:如果不涉及多个事务管理器事务的话Q不需要用JTAQ只有JDBC的事务控制就可以?/p>
b) 事务隔离U别:参见标准的SQL事务隔离U别
c) 锁的选用:悲观?一般由具体的事务管理器实现)Q对于长事务效率低,但安全。乐观锁(一般在应用U别实现)Q如在HIBERNATE中可以定?VERSION字段Q显Ӟ如果有多个应用操作数据,且这些应用不是用同一U乐观锁机制Q则乐观锁会失效。因此,针对不同的数据应有不同的{略Q同前面许多情况一P很多时候我们是在效率与安全/准确性上找一个^衡点Q无论如何,优化都不是一个纯技术的问题Q你应该对你的应用和业务特征有够的了解?/p>
9?扚w操作
即是用JDBCQ在q行大批数据更新ӞBATCH与不使用BATCH有效率上也有很大的差别。我们可以通过讄batch_size来让其支持批量操作?/p>
举个例子Q要扚w删除某表中的对象Q如“delete Account”Q打出来的语句,会发现HIBERNATE扑և了所有ACCOUNT的IDQ再q行删除Q这主要是ؓ了维护二U缓存,q样效率肯定高不了,在后l的版本中增加了bulk delete/updateQ但q也无法解决~存的维护问题。也是_׃有了二~存的维护问题,HIBERNATE的批量操作效率ƈ不尽如h?
从前面许多要点可以看出,很多时候我们是在效率与安全/准确性上找一个^衡点Q无论如何,优化都不是一个纯技术的问题Q你应该对你的应用和业务特征有够的了解Q一般的Q优化方案应在架构设计期基本确定,否则可能D没必要的q工Q致佉K目g期,而作为架构师和项目经理,q要面对开发h员可能的抱怨,必竟Q我们对用户需求更改的控制力不大,但技?架构风险是应该在初期意识到ƈ制定好相关的对策?/p>
q有一点要注意Q应用层的缓存只是锦上添花,永远不要把它当救命稻草,应用的根?数据库设计,法Q高效的操作语句Q恰当API的选择{?才是最重要的?/p>
二~存是SessionFactoryU别的全局~存Q它底下可以使用不同的缓存类库,比如ehcache、oscache{,需要用缓存实现方案?/p>
详细L: hibernate二~存ȝ
1. 如果需要特别设|ehcache的属性,把一个ehcache.xml 定义文g拯到class-path.
2. jdbc.properties加上
hibernate.cache.use_query_cache=true
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
3. applicationContext.xml?<property name="hibernateProperties">下加?/p>
<prop key="hibernate.cache.use_query_cache">$ {hibernate.cache.use_query_cache}</prop> <prop key="hibernate.cache.provider_class">$ {hibernate.cache.provider_class}</prop>
Entity二~存是把PO放进cacheQ缓存的key是IDQvalue是POJOQ不同于查询的缓存?/p>
Entity二~存在BookDao.get(3)Q和book.getCategory()的情况下都能用到Q把categoryq样的小表完全cache到内存挺不错的?/p>
在hbm文g?
<class name="Product" table="PRODUCT" dynamic-insert="true" dynamic-update="true"> <cache usage="nonstrict-read-write"/> <id name="id" column="ID"> <generator class="native"/> </id>
如果你用的二~存实现是ehcache的话Q需要配|ehcache.xml Q否则就会用ehcache默认的配|?/p>
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
上面的二U缓存是针对单个对象的,如果要对查询l果Q则是用下面的语?/p>
query.setCacheable(true);//ȀzL询缓? query.setCacheRegion("myCacheRegion");//指定要用的cacheRegionQ可?/span>
cacheRegion是可选的Q可以对使用的缓存进行特D设|, 在ehcahe.xml里要补上下面的一Dc?/p>
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
2Q?依赖容器的参数化事务理
通过容器提供的集U式参数化事务机Ӟ实现事务的外部管理,如EJB 中的事务理模式?br> 容器理的参数化事务为程序开发提供了相当的灵zL,同时因ؓ事务委托给容器q行理Q?br>应用逻辑中无需再编写事务代码,大大节省了代码量Q特别是针对需要同时操作多个事务资源的应用Q,从而提高了生率?br>
它有两种配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具体参看SpringSide)Q功能相同,只是一个在web.xml配置Q另一个在application.xml配置而已?/p>
Open Session In View在request把sessionl定到当前thread期间一直保持hibernate session在open状态,使session在request的整个期间都可以使用Q如在View层里PO也可以lazy loading数据Q如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter的doFilterҎ或Interceptor的postHandleҎ自动关闭session?/p>
OpenSessionInViewInterceptor配置
<beans>
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
...
</property>
</bean>
...
</beans>
OpenSessionInViewFilter配置
<web-app>
...
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<!-- singleSession默认为true,若设为false则等于没用OpenSessionInView -->
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
...
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
...
</web-app>
很多人在使用OpenSessionInViewq程中提及一个错误:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations
are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into
FlushMode.AUTO or remove 'readOnly' marker from transaction definition
看看OpenSessionInViewFilter里的几个Ҏ
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException { SessionFactory sessionFactory = lookupSessionFactory(); logger.debug("Opening Hibernate Session in OpenSessionInViewFilter"); Session session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource( sessionFactory, new SessionHolder(session)); try { filterChain.doFilter(request, response); } finally { TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing Hibernate Session in OpenSessionInViewFilter"); closeSession(session, sessionFactory); }}
protected Session getSession(SessionFactory sessionFactory)throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.NEVER); return session;}
protected void closeSession(Session session, SessionFactory sessionFactory)throws CleanupFailureDataAccessException { SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);}
关于l定session的方式,通过看spring里TransactionSynchronizationManager的实玎ͼ发现Q它l护一个java.lang.ThreadLocalcd的resourcesQresources负责持有U程局部变量,q里resources持有的是一个HashMapQ通过TransactionSynchronizationManager.bindResource()Ҏ在map里绑定和U程相关的所有变量到他们的标识上Q包括如上所q的l定在sessionFactory上的U程局部session。sessionHolder只不q是存放可以hold一个sessionq可以和transtaction同步的容器。可以看到OpenSessionInViewFilter在getSession的时?会把获取回来的session的flush mode 设ؓFlushMode.NEVER。然后把该sessionFactoryl定到TransactionSynchronizationManagerQrequest的整个过E都使用同一个sessionQ在hq后再接除该sessionFactory的绑定,最后closeSessionIfNecessaryҎ该session是否已和transactionl定来决定是否关闭session。绑定以后,可以防止每ơ不会新开一个Session呢?看看HibernateDaoSupport的情况:
1. public final void setSessionFactory(SessionFactory sessionFactory) { 2. this.hibernateTemplate = new HibernateTemplate(sessionFactory); 3. } 4. protected final HibernateTemplate getHibernateTemplate() { 5. return hibernateTemplate; 6. }
我们的DAO用这个templateq行操作.
public abstract class BaseHibernateObjectDao extends HibernateDaoSupport
implements BaseObjectDao {
protected BaseEntityObject getByClassId(final long id) {
BaseEntityObject obj =(BaseEntityObject) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
return session.get(getPersistentClass(),new Long(id));
}
});
return obj;
}
public void save(BaseEntityObject entity) {
getHibernateTemplate().saveOrUpdate(entity);
}
public void remove(BaseEntityObject entity) {
try {
getHibernateTemplate().delete(entity);
} catch (Exception e) {
throw new FlexEnterpriseDataAccessException(e);
}
}
public void refresh(final BaseEntityObject entity) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
session.refresh(entity);
return null;
}
});
}
public void replicate(final Object entity) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)throws HibernateException {
session.replicate(entity,ReplicationMode.OVERWRITE);
return null;
}
});
}
}
而HibernateTemplate试图每次在execute之前去获得SessionQ执行完力争关闭Session
1. public Object execute(HibernateCallback action) throws DataAccessException { 2. Session session = (!this.allowCreate ? 3. SessionFactoryUtils.getSession(getSessionFactory(), 4. false) : 5. SessionFactoryUtils.getSession(getSessionFactory(), 6. getEntityInterceptor(), 7. getJdbcExceptionTranslator())); 8. boolean existingTransaction = 9. TransactionSynchronizationManager.hasResource(getSessionFactory()); 10. if (!existingTransaction && getFlushMode() == FLUSH_NEVER) { 11. session.setFlushMode(FlushMode.NEVER); 12. } 13. try { 14. Object result = action.doInHibernate(session); 15. flushIfNecessary(session, existingTransaction); 16. return result; 17. } 18. catch (HibernateException ex) { 19. throw convertHibernateAccessException(ex); 20. } 21. finally { 22. SessionFactoryUtils.closeSessionIfNecessary( 23. session, getSessionFactory()); 24. } 25. }
而这个SessionFactoryUtils能否得到当前的session以及closeSessionIfNecessary是否真正关闭sessionQ端取决于这个session是否用sessionHolder和这个sessionFactory在我们最开始提到的TransactionSynchronizationManagerl定?
public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
if (session == null || TransactionSynchronizationManager.hasResource(sessionFactory)) {
return;
}
logger.debug("Closing Hibernate session");
try {
session.close();
}
catch (JDBCException ex) {
// SQLException underneath
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());
}
catch (HibernateException ex) {
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex);
}
}
在这个过E中Q若HibernateTemplate 发现自当前session有不是readOnly的transactionQ就会获取到FlushMode.AUTO SessionQҎ拥有写权限。也xQ如果有不是readOnly的transaction可以由Flush.NEVER转ؓFlush.AUTO,拥有insert,update,delete操作权限Q如果没有transactionQƈ且没有另外h为地设flush model的话Q则doFilter的整个过E都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有?/p>
可能的解決方式有Q?br>1、将singleSession设ؓfalseQ这样只要改web.xmlQ缺ҎHibernate Session的Instance可能会大增,使用的JDBC Connection量也会大增,如果Connection Pool的maxPoolSize讑־太小Q很Ҏ出问题?br>2、在控制器中自行理Session的FlushModeQ麻烦的是每个有Modify的Method都要多几行程式?br> session.setFlushMode(FlushMode.AUTO);
session.update(user);
session.flush();
3、Extend OpenSessionInViewFilterQOverride protected Session getSession(SessionFactory sessionFactory)Q将FlushMode直接改ؓAuto?br>4、让Ҏ受Spring的事务控制。这是怋用的ҎQ?
采用spring的事务声?使方法受transaction控制
<bean id="baseTransaction" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> <property name="transactionManager" ref="transactionManager"/> <property name="proxyTargetClass" value="true"/> <property name="transactionAttributes"> <props> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="add*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="remove*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
<bean id="userService" parent="baseTransaction"> <property name="target"> <bean class="com.phopesoft.security.service.impl.UserServiceImpl"/> </property> </bean>
对于上例Q则以save,add,update,remove开头的Ҏ拥有可写的事务,如果当前有某个方法,如命名ؓimportExcel()Q则因没有transaction而没有写权限Q这时若Ҏ内有insert,update,delete操作的话Q则需要手动设|flush model为Flush.AUTO,?/p>
session.setFlushMode(FlushMode.AUTO);
session.save(user);
session.flush();
管Open Session In View看v来还不错Q其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternalҎ代码Q这个方法实际上是被父类的doFilter调用的,因此Q我们可以大U了解的OpenSessionInViewFilter调用程: request(h)->open sessionq开始transaction->controller->View(Jsp)->l束transactionqclose session.
一切看h很正,其是在本地开发测试的时候没出现问题Q但试想下如果流E中的某一步被d的话Q那在这期间connection׃直被占用而不释放。最有可能被d的就是在写Jspq步Q一斚w可能是页面内容大Qresponse.write的时间长Q另一斚w可能是网速慢Q服务器与用户间传输旉久。当大量q样的情况出现时Q就有连接池q接不Q造成面假死现象?/p>
Open Session In View是个双刃剑,攑֜公网上内容多量大的|站h用?/p>
另外Q这样会产生一点危险性,毕竟把数据库讉K的环境放C表现层。(用VOQ?br>
Query queryObject = session.createSQLQuery( "select count(*) as count" + " from " + tableName + " record where record.userid = :userid"). addScalar("count", Hibernate.INTEGER); |
(1)当用某个重载的SessionFactory.openSession()使用Interceptor作ؓ参数调用打开一个session的时候,指定了Session范围内的拦截器?
Session session = sf.openSession( new AuditInterceptor() );
(2)SessionFactory范围内的拦截器要通过Configuration中注册,而这必须在创?tt class="literal">SessionFactory之前。在q种情况下,l出的拦截器会被q个SessionFactory所打开的所有session使用了;除非session打开时明指明了使用的拦截器?tt class="literal">SessionFactory范围内的拦截器,必须是线E安全的Q因为多个session可能q发使用q个拦截器,要因此小心不要保存与session相关的状态?
new Configuration().setInterceptor( new AuditInterceptor() );package com.jason.interceptor;
import java.io.Serializable;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class UserInterceptor extends EmptyInterceptor {private int updates;
private int creates;
private int loads;public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {if ( entity instanceof User ) {
updates++;
}
return false;
}public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof User ) {
loads++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "password".equals( propertyNames[i] ) ) {
String temp = (state[i]).toString();
state[i] = temp.substring(3, temp.length()-3);
return true;
}
}
}
return false;
}public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {if ( entity instanceof User ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "password".equals( propertyNames[i] ) ) {
state[i] = "abc" +state[i] + "xyz";
return true;
}
}
}
return false;
}public void afterTransactionCompletion(Transaction tx) {
if ( tx.wasCommitted() ) {
System.out.println("Creations: "
+ creates + ", Updates: " + updates + ",Loads: " + loads);
}
updates=0;
creates=0;
loads=0;
}}
【注】String[] propertyNames -- 属性名U?br /> Object[] state -- 属性对应的?br />
test:
Session session = sf.openSession(new UserInterceptor());
session.load(class, id);
session.save(obj);
1、创Z个Criteria实例
Criteria crit = sess.createCriteria(Cat.class);
List cats = sess.createCriteria(Cat.class)
List cats = sess.createCriteria(Cat.class)
List cats = sess.createCriteria(Cat.class)
3、对l果排序
List cats = sess.createCriteria(Cat.class)
List cats = sess.createCriteria(Cat.class) 下面的替代Ş式在特定情况下有用。?/font>
List cats =
sess.createCriteria(Cat.class) h意,前面两个查询中Cat实例所持有的kittens集合cdƈ没有通过criteria预先qoQ如果你希望只返回满x件的kittens,你必M用returnMaps()。?/font>
List cats = sess.createCriteria(Cat.class)
List cats = sess.createCriteria(Cat.class)
6、根据示例查询(Example queriesQ?/strong>
Cat cat = new Cat(); You can adjust how the Example is applied. 你可以调整示?Example)如何应用。?/font>
Example example = Example.create(cat)
List results = session.createCriteria(Cat.class) |
//HQL-Delete
String hql = "delete from Product where name = :name";
Query query = session.createQuery(hql);
query.setString("name","Product 1");
int rowCount = query.executeUpdate();
//HQL-Function
String hql = "select min(product.price), max(product.price) from Product product";
Query query = session.createQuery(hql);
List results = query.list();
//HQL-Fetch Associations HQL Inner Join
String hql = "from Supplier s inner join fetch s.products as p";
Query query = session.createQuery(hql);
List results = query.list();
//HQL-Named Parameters
String hql = "from Product where price > :price";
Query query = session.createQuery(hql);
query.setDouble("price",2.0);
List results = query.list();
String hql = "from Product as product where product.supplier=:supplier";
Query query = session.createQuery(hql);
query.setEntity("supplier",supplier);
List results = query.list();
//HQL-Update
String hql = "update Supplier set name = :newName where name = :name";
Query query = session.createQuery(hql);
query.setString("name","Supplier Name 1");
query.setString("newName","s1");
int rowCount = query.executeUpdate();
//HQL-where
String hql = "from Product where price > 2.0 and name like 'P%'";
Query query = session.createQuery(hql);
List results = query.list();
//HQL-Map
String hql = " select new map(usr.name as userName, usr.password as password) from User usr";
Query query = session.createQuery(hql);
List list = query.list();
Map goods =(Map)list.get(0);
【注?br />
String hql = " select new map(usr.name as userName, usr.password as password) from com.jason.User usr";
String hql = " select new map(usr.name as userName, usr.password as password) from com.jason.User usr";
׃from之前的空|引vunexpected token: from
查询语句可以q回gؓMcd的属性或对象Q包括返回类型ؓ某种lg(Component)的属? 查询语句可以q回多个对象和(或)属性,存放?span class="Apple-converted-space"> 或存攑֜一?tt class="literal">new list(mother, offspr, mate.name) 也可能直接返回一个实际的cd安全的Java对象(假设c?tt class="literal">new Family(mother, mate, offspr) 也可以用关键字max, min(bodyWeight) as min, count(*) as n
|
<!--
create table t_orderdetail ( order_id bigint not null, item_id bigint not null, count bigint not null, primary key(order_id, item_id) )
-->
<set name="events" cascade="all">
<key column="PERSON_ID"/>
<one-to-many class="com.jason.collections.set.Event"/>
<!--<element column="title" type="string" />-->
</set>
</class>
<class name="com.jason.collections.set.Event" table="EVENT">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="title"/>
</class>
<!--
CREATE TABLE person (person_Id int PRIMARY KEY, person_Name varchar(20))
CREATE TABLE Event (Event_Id int PRIMARY KEY, PERSON_ID int not null, title varchar2(20))
-->
-->
<set name="addresses" table="PersonAddress" cascade="all">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="com.jason.associations.one2many.unilateralism.one2many.table.Address"/>
</set>
//set,Array,bag,map都行
<set name="addresses" table="PersonAddress" cascade="all">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="com.jason.associations.one2many.unilateralism.one2many.table.Address"/>
</set>
<many-to-one name="address"
class="com.jason.associations.one2one.unilateralism.foreign.Address"
unique="true"
column="addressId"
cascade="all"
not-null="true"/>
<one-to-one name="person"
class="com.jason.associations.one2one.unilateralism.primary.Person"
constrained="true"/>
1、都表示一对一 ,many-to-one指定了对应的columnQ和column相关的属性cascadeQnot-null...Q?br /> 而one-to-one没有对应的columnQ它主要用于id的generator class="foreign"?br />2、同样也适用?lt;many-to-many unique="true">?lt;one-to-many>
<one-to-many>
<many-to-one>
不管哪个Q对应的表是q样的,把one那张表的keyQ放到many对应的表中,相当于外键?/p>
<set name="addresses" cascade="all">
<!--key 是Address表的字段Q最后要update Address?personId = Person?personId-->
<key column="personId" not-null="true"/>
<one-to-many
class="com.jason.associations.one2many.unilateralism.one2many.Address"/>
</set>
<id name="id" column="addressId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
class="com.jason.associations.one2one.unilateralism.primary.Person"
constrained="true"/>
<one-to-many>?lt;one-to-one>都没有column属性,因ؓone对应的那边不需要相应的列,
那么如何保持关系呢?
1、最好不使用Q?img height="19" src="http://www.tkk7.com/Emoticons/emsmilep.gif" width="19" border="0" />
2、向上面那样指定一?/p>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class>
1、ؓ了更好地提高执行效率Q需要将“多”的一端设Z控方Q就是将兌关系的维护工作交l“多”的一端。inverse="true" 说明L表是Person表(Address表对面)inverse="false"本nQ默认|false
2?font face="Courier New">inverse="true"可以出现在关联的L一端,即collection端或者join端?/p>
2、用连接表的双向多对多兌
<class name="com.jason.associations.many2many.both.Person" table="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress" cascade="all">
<key column="personId" not-null="true"/>
<many-to-many
column="addressId"
class="com.jason.associations.many2many.both.Address"/>
</set>
<property name="name" column="personName" type="string" />
</class>
<class name="com.jason.associations.many2many.both.Address" table="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="persons" table="PersonAddress" inverse="false" cascade="all">
<key column="addressId" not-null="true"/>
<many-to-many
column="personId"
class="com.jason.associations.many2many.both.Person"/>
</set>
<property name="country" column="country" type="string" />
<property name="city" column="city" type="string" />
</class>
<!--
create table Person ( personId bigint not null primary key, personName varchar(20))
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId))
create table Address ( addressId bigint not null primary key, country varchar(20), city varchar(20) )
-->
1、基于外键关联的单向一对一兌
和单向多对一兌几乎是一L。唯一的不同就是单向一对一兌中的外键字段h唯一性约束?
<class name="com.jason.associations.one2one.unilateralism.foreign.Person" table="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
class="com.jason.associations.one2one.unilateralism.foreign.Address"
column="addressId"
unique="true"
cascade="all"
not-null="true"/>
</class>
<class name="com.jason.associations.one2one.unilateralism.foreign.Address" table="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
<!--
create table Person ( personId bigint not null primary key, addressId bigint not null unique)
create table Address ( addressId bigint not null primary key)
-->
2、基于主键关联的单向一对一兌
通常使用一个特定的id生成器。(h意,在这个例子中我们掉换了关联的方向。)
<class name="com.jason.associations.one2one.unilateralism.primary.Person" table="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="com.jason.associations.one2one.unilateralism.primary.Address" table="Address">
<id name="id" column="addressId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
class="com.jason.associations.one2one.unilateralism.primary.Person"
constrained="true"/>
</class>
<!--
create table Person ( personId bigint not null primary key)
create table Address ( addressId bigint not null primary key)
其中personId= addressId
-->
3、基于连接表的单向一对一兌非常见Q但也是可行的?
<class name="com.jason.associations.one2one.unilateralism.table.Person" table="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<!--join 必须攑֜最下面-->
<join table="PersonAddress" optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
cascade="all"
unique="true"/>
</join>
</class>
<class name="com.jason.associations.one2one.unilateralism.table.Address" table="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
<!--
create table Person ( personId bigint not null primary key)
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key)
-->
4、对应的双向兌
a、单向关联和双向兌使用的表是一L
b、双向关联类中多了属?br />c、双向关联可以从M一个对象开始操作(比如dQ,而单向只能从特定的一Ҏ?/p>
User.java
package com.jason.component;
public class User{
private Integer id;
private String name;
private String password;
private Contact contact;
/**
* @return Returns the id.
*/
public Integer getId() {
return id;
}
/**
* @param id The id to set.
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the password.
*/
public String getPassword() {
return password;
}
/**
* @param password The password to set.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return Returns the contact.
*/
public Contact getContact() {
return contact;
}
/**
* @param contact The contact to set.
*/
public void setContact(Contact contact) {
this.contact = contact;
}
}
Contact.java
package com.jason.component;
public class Contact {
private String email;
private String address;
/**
* @return Returns the address.
*/
public String getAddress() {
return address;
}
/**
* @param address The address to set.
*/
public void setAddress(String address) {
this.address = address;
}
/**
* @return Returns the email.
*/
public String getEmail() {
return email;
}
/**
* @param email The email to set.
*/
public void setEmail(String email) {
this.email = email;
}
}
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"<hibernate-mapping package="com.jason.component">
<class
name="User"
table="user"
>
<meta attribute="sync-DAO">false</meta><id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id><property
name="Name"
column="name"
type="string"
not-null="true"
length="20"
/>
<property
name="Password"
column="password"
type="string"
not-null="true"
length="20"
/><component class="Contact" name="Contact">
<property
column="email"
length="50"
name="Email"
not-null="false"
type="string"
/>
<property
column="address"
length="100"
name="Address"
not-null="false"
type="string"
/>
</component></class>
</hibernate-mapping>
testComponent.java
package com.jason.component;
import java.util.List;
import java.util.ListIterator;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class testComponent {
public static void main(String[] args) throws HibernateException {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
testInsert(sessionFactory);
testQuery(sessionFactory);
sessionFactory.close();
}
public static void testInsert( SessionFactory sessionFactory )throws HibernateException {
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
User user = new User();
Contact contact=new Contact();
contact.setEmail("gm_jing@yahoo.com.cn");
contact.setAddress("beijing");
user.setName("gm_jing");
user.setPassword("1111111");
user.setContact(contact);
session.save(user);
tx.commit();
session.close();
System.out.println("OKQ?);
}
public static void testQuery( SessionFactory sessionFactory )throws HibernateException {
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
User user = new User();
Query query=session.createQuery("from User as user");
//query.setCharacter(1, 'M');
List names =query.list();
for(ListIterator it=names.listIterator();it.hasNext();){
user= (User)it.next();
System.out.println("Id: " + user.getId());
System.out.println("name: " + user.getName());
System.out.println("password: " + user.getPassword());
if(user.getContact()!=null){
if(user.getContact().getEmail()!=null){
System.out.println("Email: " + user.getContact().getEmail());
}
if(user.getContact().getAddress()!=null){
System.out.println("Address: " + user.getContact().getAddress());
}
}
}
tx.commit();
session.close();
}
}
像所有的值类型一? lg不支持共享引用?换句话说Q两个Contact可能相同Q但是两个User对象应该包含两个独立的Contact对象Q只不过q两个Contact对象h“同样”的倹{?lg的值可以ؓI,其定义如下?每当Hibernate重新加蝲一个包含组件的对象,如果该组件的所有字DؓI,Hibernate假定整个组件ؓI?在大多数情况?q样假定应该是没有问题的?
每个cd层结构一张表
每个子类一个表
每个具体cM个表
表的个数
1
n
n-1
父表
一个表
1
0
子表
n-1
n-1
表的字段
每个cȝ所有属性+cd鉴别
父表
-->
父类属?/span>
子表
-->
子类属性+父类
id
子表
-->
子类属性+父类属?/span>
表的兌
/
表的唯一
cd鉴别标识
父类
id
id
package com.jason.inheritance.three;
public abstract class Person {
private Long id;
private String name;
/**
* @hibernate.id
* column="ID"
* generator-class="hilo"
* unsaved-value="null"
*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @hibernate.property
* length = "24"
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.jason.inheritance.three;
public class Professor extends Person {
private int salary;/**
* @hibernate.property
*/
public int getSalary() {
return salary;
}public void setSalary(int salary) {
this.salary = salary;
}
}
package com.jason.inheritance.three;
public class Student extends Person {
private String studentNumber;
/**
* @hibernate.property
* length = "24"
*/
public String getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
}
昄对应的表?br /> professor(id, name, salary)
student(id, name, studentNumber)
插入记录的sql如下Q?br /> Hibernate: insert into Student (studentNumber, name, ID) values (?, ?, ?)
package com.jason.inheritance.two;
public class Vehicle {
private Long id;
private String name;
/**
* @hibernate.id
* column="ID"
* generator-class="hilo"
* unsaved-value="null"
*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @hibernate.property
* length = "24"
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.jason.inheritance.two;
public class Car extends Vehicle {
private String seat;
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
package com.jason.inheritance.two;
public class Truck extends Vehicle {
private String load;
public String getLoad() {
return load;
}
public void setLoad(String load) {
this.load = load;
}
}
昄对应的表?br />
vehicle(id, name) car(id, seat) truck(id, loads)
需要注意的是:car/truck 中的id 和vehicle中的id是一一对应的?/font>
/*
Truck truck = new Truck();
truck.setName("襉K卡R");
truck.setLoad("10ton");
sess.save(truck);*/
插入记录的sql如下Q?br />
Hibernate: insert into Vehicle (name, ID) values (?, ?)
Hibernate: insert into Truck (loads, id) values (?, ?)
保存一条truck信息Q要先在Vehicle中保存记录,得到idQ然后把得到的id和需要的g同保存到Truck中?br />
参考:
Animal.java
package com.jason.inheritance;
/**
* @hibernate.class
* table="Animal"
* discriminator-value="Animal"
* @hibernate.discriminator
* column="ANIMAL_TYPE"
* type="string"
* length = "10"
*/
public class Animal {
private Long id;
private String name;
/**
* @hibernate.id
* column="ID"
* generator-class="hilo"
* unsaved-value="null"
*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @hibernate.property
* length = "24"
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void makeSound(){};
}
package com.jason.inheritance;
public class Cat extends Animal { privateString FurColor; publicvoid makeSound() { System.out.println("喵喵"); } publicString getFurColor() { return FurColor; } publicvoid setFurColor(String furColor) { FurColor = furColor; } }
package com.jason.inheritance;
/**
* @hibernate.subclass
* discriminator-value="Dog"
*/
public class Dog extends Animal {
private String category;
public void makeSound() {
System.out.println("汪汪");
}
/**
* @hibernate.property
* length = "24"
*/
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
昄对应的表是AnimalQID, ANIMAL_TYPE, name, furColor, categoryQ?br />
需要在表中使用识别字段来表C某一列(rowQ是属于某个子类别或父类?
是用ANIMAL_TYPE来表C的Q?br />
插入记录的sql如下Q?br />
Hibernate: insert into Animal (name, ANIMAL_TYPE, ID) values (?, 'Animal', ?)
Hibernate: insert into Animal (name, furColor, ANIMAL_TYPE, ID) values (?, ?, 'Cat', ?)
Hibernate: insert into Animal (name, category, ANIMAL_TYPE, ID) values (?, ?, 'Dog', ?)
参考:
Hibernate 在其数据讉K引擎中内|了乐观锁实现。如果不用考虑外部pȝҎ据库的更新操作,
利用Hibernate提供的透明化乐观锁实现Q将大大提升我们的生产力?br />Hibernate中可以通过class描述W的optimistic-lock属性结合version描述W指定?br />现在Q我们ؓ之前CZ中的TUser加上乐观锁机制?br />1Q?首先为TUser的class描述W添加optimistic-lock属性:
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
…?br />
Ø none
无乐观锁
Ø version
通过版本机制实现乐观?br />Ø dirty
通过查发生变动过的属性实C观锁
Ø all
通过查所有属性实C观锁
其中通过version实现的乐观锁机制是Hibernate官方推荐的乐观锁实现Q同时也
是Hibernate中,目前唯一在数据对象脱Session发生修改的情况下依然有效的锁?br />制。因此,一般情况下Q我们都选择version方式作ؓHibernate乐观锁实现机制?/p>
Session.find/iterate的区别:
findҎ执行Select SQL从数据库中获得所有符合条件的记录q构造相应的实体对象Q实体对象构建完毕之后,将其纳入缓存。它对缓存只写不读,因此无法利用~存?br />
iterateҎ首先执行一条Select SQL以获得所有符合查询条件的数据idQ随卻IiterateҎ首先在本地缓存中Ҏid查找对应的实体对象是否存在,如果~存中已l存在对应的数据Q则直接以此数据对象作ؓ查询l果Q如果没有找刎ͼ再执行相应的Select语句获得对应的库表记录(iterateҎ如果执行了数据库d操作q构Z完整的数据对象,也会其查询l果U_~存Q?
[实际的情冉|,如果使用了iterateҎq回Iteratorcd的查询结?那么你一旦关闭session,Iterator中的数据立即׃消失.而通过find得到的List则不会如?我想大部分h的用习惯都是操作完成后立即关闭session,很多公司也强制要求这样做.
Iterator的另一个麻烦事是fail-fast,在多U程环境?很容易?使用U程安全的List子类,则不会有q个问题]
Query Cache产生作用的情况:
1.完全相同的Select SQL重复执行?br />
2.在两ơ查询之_此Select SQL对应的库表没有发生过改变?
Session.saveҎ的执行步骤:
1.在Session内部~存中寻扑־保存对象。内部缓存命中,则认为此数据已经保存Q执行过insert操作Q,实体对象已经处于Persistent状态,直接q回?br />
2.如果实体cdClifecycle接口Q则调用待保存对象的onSaveҎ?br />
3.如果实体cdCvalidatable接口Q则调用其validate()Ҏ?br />
4.调用对应拦截器的Interceptor.onSaveҎQ如果有的话Q?br />
5.构造Insert SQLQƈ加以执行?br />
6.记录插入成功Quser.id属性被讑֮为insert操作q回的新记录id倹{?br />
7.user对象攑օ内部~存?br />
8.最后,如果存在U联关系Q对U联关系q行递归处理?
Session.updateҎ的执行步骤:
1.Ҏ待更新实体对象的KeyQ在当前session的内部缓存中q行查找Q如果发玎ͼ则认为当前实体对象已l处于Persistent状态,q回?br />
2.初始化实体对象的状态信息(作ؓ之后脏数据检查的依据Q,q将其纳入内部缓存。注意这里Session.updateҎ本nq没有发送Update SQL完成数据更新操作Q?span style="color: red">Update SQL在之后的Session.flushҎ中执行,Ҏid更新所有的字段Q如update user set name=?, password=? where id=??
Session.saveOrUpdateҎ的执行步骤:
1.首先在Session内部~存中进行查找,如果发现则直接返回?br />
2.执行实体cd应的Interceptor.isUnsavedҎQ如果有的话Q,判断对象是否为未保存状态?br />
3.Ҏunsaved-value判断对象是否处于未保存状态?br />
4.如果对象未保存(Transient状态)Q则调用saveҎ保存对象?br />
5.如果对象为已保存QDetached状态)Q调用updateҎ对象与Session重新兌?/p>
调用flush
1.直接调用Session.flush();
2.tx.commit();
//flush before commiting the transaction and closing the session
//Flushing is the process of synchronising the underlying persistent store with persistable state held in memory.
E1=Session.merge(E)
if E is transient instance,则insert数据?E状态不变化,E1是一个持久化对象;
if E is detached instance,则重新load,变成persistent instance;
if E is persistent instance,则从~存中选择;
// Copy the state of the given object onto the persistent object with the same identifier.
// If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance.
// If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session.
// This operation cascades to associated instances if the association is mapped with cascade="merge".
persist() is well defined. It makes a transient instance persistent. However,
it doesn't guarantee that the identifier value will be assigned to the persistent
instance immediately, the assignment might happen at flush time. The spec doesn't say
that, which is the problem I have with persist().
persist() also guarantees that it will not execute an INSERT statement if it is
called outside of transaction boundaries. This is useful in long-running conversations
with an extended Session/persistence context.A method like persist() is required.
save() does not guarantee the same, it returns an identifier, and if an INSERT
has to be executed to get the identifier (e.g. "identity" generator, not "sequence"),
this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is not good in a long-running conversation with an extended Session/persistence context."