??xml version="1.0" encoding="utf-8" standalone="yes"?> 1、自动增长identity 适用于MySQL、DB2、MS SQL ServerQ采用数据库生成的主键,用于为long、short、intcd生成唯一标识 <id name="id" column="id" type="long"> DB2、Oracle均支持的序列Q用于ؓlong、short或int生成唯一标识 <id name="id" column="id" type="long"> 3、hilo 使用一个高/低位法生成的long、short或intcd的标识符Q给定一个表和字D作为高位值的来源Q默认的表是hibernate_unique_keyQ默认的字段是next_hi。它?yu)id的生源分成两部分,DB+内存Q然后按照算法结合在一起生id|可以在很的q接ơ数内生多条记录,提高效率 MySQLQcreate table hi_value(next_hi integer not null); insert into hi_value(next_hi) values(1); <id name="id" column="id"> 会根据底层数据库的能力,从identity、sequence、hilo中选择一个,灉|性更强,但此Ӟ如果选择sequence或者hiloQ则所有的表的主键都会从Hibernate默认的sequence或者hilo表中取。ƈ且,有的数据库对于默认情况主键生成测试的支持Q效率ƈ不是很高 <id name="id" column="id"> sequence和hilo的结合,hilo的高位由sequence产生Q所以也需要底层数据库的支?br />通过hilo法实现Q但是主键历史保存在Sequence中,适用于支?Sequence 的数据库Q如 OracleQ比较少用) <id name="id" column="id"> q个是由Hibernate在内存中生成主键Q每ơ增量ؓ1Q不依赖于底层的数据库,因此所有的数据库都可以使用Q但问题也随之而来Q由于是Hibernate生成的,所以只 能有一个Hibernate应用q程讉K数据库,否则׃产生主键冲突Q不能在集群情况下?br />插入数据的时候hibernate会给主键d一个自增的主键Q但是一个hibernate实例q护一个计数器Q所以在多个实例q行的时候不能用这个方?br /><id name="id" column="id"> uuid.hex 使用一?28-bit的UUID法生成字符串类型的标识W,UUID被编码成一?2?6q制数字的字W串。UUID包含QIP地址、JVM启动旉、系l时__?/4U)和一个计数器|JVM中唯一Q?br />hibernate会算Z?28位的唯一值插?/p>
<id name="id" column="id"> 由应用程序负责生成主键标识符Q往往使用在数据库中没有代理主键,使用的主键与业务相关的情况,如: <id name="id" column="id" type="string"> 10、select ps: 代理主键是指与业务无关且能唯一标识数据库中记录,一般是数据库自动生成的,比如mysql可以使用auto_increment,Sql2000可以使用identity生成方式,oracle可以使用sequence生成方式自然主键指业务相?qh?且能唯一标识数据库中的Q意一条记?/span> 介版Q?/span> incrementQ代理主键,适合于所有数据库Q由hibernatel护主键自增Q和底层数据库无养I但是不适合?个或以上hibernateq程?/p>
identityQ代理主键,适合于Mysql或ms sql server{支持自增的dbmsQ主键g由hibernatel护?/span> sequenceQ代理主键,适合于oracle{支持序列的dbmsQ主键g由hibernatel护Q由序列产生?/span> nativeQ代理主键,Ҏ(gu)底层数据库的具体Ҏ(gu)选择适合的主键生成策略,如果是mysql或sqlserverQ选择identityQ如果是oracleQ选择sequence?/p>
hiloQ代理主键,hibernate把特定表的字D作为hign|生成主键?/p>
uuid.hexQ代理主键,hibernate采用uuid 128位算法生成基于字W串的主键?/p>
assignQ适合于应用程序维护的自然主键?/p>
使用SQL Server ?MySQL 的自增字D,q个Ҏ(gu)不能攑ֈ Oracle 中,Oracle 不支持自增字D,要设定sequenceQMySQL ?SQL Server 中很常用Q?br />数据库中的语法如下:
MySQLQcreate table t_user(id int auto_increment primary key, name varchar(20));
SQL ServerQcreate table t_user(id int identity(1,1) primary key, name varchar(20));
<generator class="identity" />
</id>
2、sequence
数据库中的语法如下:
OracleQcreate sequence seq_name increment by 1 start with 1;
需要主键值时可以调用seq_name.nextval或者seq_name.curval得到Q数据库会帮助我们维护这个sequence序列Q保证每ơ取到的值唯一Q如Q?br />insert into tbl_name(id, name) values(seq_name.nextval, ‘Jimliu’);
<generator class="sequence">
<param name="sequence">seq_name</param>
</generator>
</id>
如果我们没有指定sequence参数Q则Hibernate会访问一个默认的sequenceQ是hibernate_sequenceQ我们也需要在数据库中建立q个sequence
此外Qsequenceq可以有另外一个参数是paramtersQ可以查看Hibernate的API了解它的用法Q见org.hibernate.id.SequenceGenerator
调用数据库的sequence来生成主键,要设定序列名Q不然hibernate无法扑ֈQ?br /><param name="sequence">NAME_SEQ</param>QOracle中很常用Q?/p>
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
</id>
在hibernate持久化的时候,由hibernate负责生成低位倹{hilo标识W生成器在生成标识符旉要从hi_value表中取出next_hi的当前|然后修改该|q个操作是在单独的事务中完成的。最大的低值在属性max_lo中配|,但在Hibernate内存中生成的低位Dq此值时Q就有需要到数据库的hi_value表中再次d高位g
使用hilo生成{略Q要在数据库中徏立一张额外的表,默认表名为hibernate_unique_key,默认字段为integercdQ名U是next_hiQ比较少用)
我们也可以自p|自定义的表名和字段?br /><id name="id" type="integer">
<column name="id"/>
<generator class="hilo">
<param name="my_unique_key"/>
<param column="next_hi"/>
</generator>
</id>
4、native
对于 oracle 采用 Sequence 方式Q对于MySQL ?SQL Server 采用identityQ自增主键生成机ӞQnative是主键的生成工作交由数据库完成,hibernate不管Q很常用Q?/p>
<generator class="native" />
</id>
5、seqhilo
<generator class="seqhilo">
<param name="sequence">seq_name</param>
<param name="max_lo">100</param>
</generator>
</id>
6、increment
<generator class="increment" />
</id>
7?/p>
<generator class="uuid.hex" />
</id>
uuid.string
hibernate会算Z?6位的值插?/p>
8、assigned
<generator class="assigned" />
</id>
q种主键的生成方式不使用Q在数据库表设计时就应该使用代理主键Qsurrogate keyQ,不应使用自然主键Qnatural keyh业务含义Q,在没有指?lt;generator>标签Ӟ默认是assigned主键的生成方?br />在插入数据的时候主键由用户自己dQhibernate也不?/p>
9、foreign
使用外部表的字段作ؓ主键
使用触发器生成主键(主要用于早期的数据库主键生成机制Q少用)
Why are equals() and hashcode() important
Normally, most Java objects provide a built-in equals() and hashCode() based on the object's identity; so each new() object will be different from all others.
This is generally what you want in ordinary Java programming. And if all your objects are in memory, this is a fine model. Hibernate's whole job, of course, is to move your objects out of memory. But Hibernate works hard to prevent you from having to worry about this.
Hibernate uses the Hibernate session to manage this uniqueness. When you create an object with new(), and then save it into a session, Hibernate now knows that whenever you query for an object and find that particular object, Hibernate should return you that instance of the object. And Hibernate will do just that.
However, once you close the Hibernate session, all bets are off. If you keep holding onto an object that you either created or loaded in a Hibernate session that you have now closed, Hibernate has no way to know about those objects. So if you open another session and query for "the same" object, Hibernate will return you a new instance. Hence, if you keep collections of objects around between sessions, you will start to experience odd behavior (duplicate objects in collections, mainly).
The general contract is: if you want to store an object in a List, Map or a Set then it is an requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.
What is the problem after all?
So let's say you do want to keep objects around from session to session, e.g. in a Set related to a particular application user or some other scope that spans several Hibernate sessions.
The most natural idea that comes to mind is implementing equals() and hashCode() by comparing the property you mapped as a database identifier (ie. the primary key attribute). This will cause problems, however, for newly created objects, because Hibernate sets the identifier value for you after storing new objects. Each new instance therefore has the same identifier, null (or <literal>0</literal>). For example, if you add some new objects to a Set:
// Suppose UserManager and User are Beans mapped with Hibernate
UserManager u = session.load(UserManager.class, id);
u.getUserSet().add(new User("newUsername1")); // adds a new Entity with id = null or id = 0
u.getUserSet().add(new User("newUsername2")); // has id=null, too so overwrites last added object.
// u.getUserSet() now contains only the second User
As you can see relying on database identifier comparison for persistent classes can get you into trouble if you use Hibernate generated ids, because the identifier value won't be set before the object has been saved. The identifier value will be set when session.save() is called on your transient object, making it persistent.
If you use manually assigned ids (e.g. the "assigned" generator), you are not in trouble at all, you just have to make sure to set the identifier value before adding the object to the Set. This is, on the other hand, quite difficult to guarantee in most applications.
Separating object id and business key
To avoid this problem we recommend using the "semi"-unique attributes of your persistent class to implement equals() (and hashCode()). Basically you should think of your database identifier as not having business meaning at all (remember, surrogate identifier attributes and automatically generated vales are recommended anyway). The database identifier property should only be an object identifier, and basically should be used by Hibernate only. Of course, you may also use the database identifier as a convenient read-only handle, e.g. to build links in web applications.
Instead of using the database identifier for the equality comparison, you should use a set of properties for equals() that identify your individual objects. For example, if you have an "Item" class and it has a "name" String and "created" Date, I can use both to implement a good equals() method. No need to use the persistent identifier, the so called "business key" is much better. It's a natural key, but this time there is nothing wrong in using it!
The combination of both fields is stable enough for the life duration of the Set containing your Items. It is not as good as a primary key, but it's certainly a candidate key. You can think of this as defining a "relational identity" for your object -- the key fields that would likely be your UNIQUE fields in your relational model, or at least immutable properties of your persistent class (the "created" Date never changes).
In the example above, you could probably use the "username" property.
Note that this is all that you have to know about equals()/hashCode() in most cases. If you read on, you might find solutions that don't work perfectly or suggestions that don't help you much. Use any of the following at your own risk.
Workaround by forcing a save/flush
If you really can't get around using the persistent id for equals() / hashCode(), and if you really have to keep objects around from session to session (and hence can't just use the default equals() / hashCode()), you can work around by forcing a save() / flush() after object creation and before insertion into the set:
// Suppose UserManager and User are Beans mapped with Hibernate
UserManager u = session.load(UserManager.class, id);
User newUser = new User("newUsername1");
// u.getUserSet().add(newUser); // DO NOT ADD TO SET YET!
session.save(newUser);
session.flush(); // The id is now assigned to the new User object
u.getUserSet().add(newUser); // Now OK to add to set.
newUser = new User("newUsername2");
session.save(newUser);
session.flush();
u.getUserSet().add(newUser); // Now userSet contains both users.
Note that it's highly inefficient and thus not recommended. Also note that it is fragile when using disconnected object graphs on a thin client:
// on client, let's assume the UserManager is empty:
UserManager u = userManagerSessionBean.load(UserManager.class, id);
User newUser = new User("newUsername1");
u.getUserSet().add(newUser); // have to add it to set now since client cannot save it
userManagerSessionBean.updateUserManager(u);
// on server:
UserManagerSessionBean updateUserManager (UserManager u) {
// get the first user (this example assumes there's only one)
User newUser = (User)u.getUserSet().iterator().next();
session.saveOrUpdate(u);
if (!u.getUserSet().contains(newUser)) System.err.println("User set corrupted.");
}
This will actually print "User set corrupted." since newUser's hashcode will change due to the saveOrUpdate call.
This is all frustrating because Java's object identity seems to map directly to Hibernate-assigned database identity, but in reality the two are different -- and the latter doesn't even exist until an object is saved. The object's identity shouldn't depend on whether it's been saved yet or not, but if your equals() and hashCode() methods use the Hibernate identity, then the object id does change when you save.
It's bothersome to write these methods, can't Hibernate help?
Well, the only "helping" hand Hibernate can provide is hbm2java.
hbm2java does not (anymore) generate equals/hashcode based on id's because of the described issues in this page.
You can though mark certain properties with <meta attribute="use-in-equals">true</meta> to tell hbm2java to generate a proper equals/hashcode.
Summary
To sum all this stuff up, here is a listing of what will work or won't work with the different ways to handle equals/hashCode:
no eq/hC at all eq/hC with the id property eq/hC with buisness key
use in a composite-id No Yes Yes
multiple new instances in set Yes No Yes
equal to same object from other session No Yes Yes
collections intact after saving Yes No Yes
Where the various problems are as follows:
use in a composite-id:
To use an object as a composite-id, it has to implement equals/hashCode in some way, == identity will not be enough in this case.
multiple new instances in set:
Will the following work or not:
HashSet someSet = new HashSet();
someSet.add(new PersistentClass());
someSet.add(new PersistentClass());
assert(someSet.size() == 2);
equal to same object from another session:
Will the following work or not:
PersistentClass p1 = sessionOne.load(PersistentClass.class, new Integer(1));
PersistentClass p2 = sessionTwo.load(PersistentClass.class, new Integer(1));
assert(p1.equals(p2));
collections intact after saving:
Will the following work or not:
HashSet set = new HashSet();
User u = new User();
set.add(u);
session.save(u);
assert(set.contains(u));
Any best practicies for equals and hashcode
Read the links in 'Background material' and the API docs - they provide the gory details.
Furthermore I encourage anyone with information and tips about equals and hashcode implementations to come forward and show their "patterns" - I might even try to incorporate them inside hbm2java to make it even more helpful ;)
Background material:
Effective Java Programming Language Guide, sample chapter about equals() and hashCode()
Java theory and practice: Hashing it out, Article from IBM
Sam Pullara (BEA) comments on object identity: Blog comment
Article about how to implement equals and hashCode correctly by Manish Hatwalne: Equals and HashCode
Forum thread discussing implementation possibilities without defining a business identity: Equals and hashCode: Is there *any* non-broken approach?
According to The java.lang.Object documentation it should be perfectly ok to always return 0 for the hashCode(). The positive effect of implementing hashCode() to return unique numbers for unique objects, is that it might increase performance. The downside is that the behavior of hashCode() must be consistent with equals(). For object a and b, if a.equals(b) is true, than a.hashCode() == b.hashCode() must be true. But if a.equals(b) returns false, a.hashCode() == b.hashCode() may still be true. Implementing hashCode() as 'return 0' meets these criteria, but it will be extremely inefficient in Hash based collection such as a HashSet or HashMap.
type(分类?
typeid(id主键) typename(分类名称)
supplier(供应商表)
supplierid(ID主键) suppliername( 供应商名U?
你可Z个查询条件的c,里面包括你要查询的所有字D?br />
? public class Query{
private String suppliername;
private String goodsname;
private String typename;
..................
get/setҎ(gu)................
}
得到查询条g后,可以把此cȝ一个对象传入自己做的方法,此方法可以根据条件的个数及是否输入条件进行查询:
public static List query_goods(Query query){
Session session = SessionFactory.getSession();
Criteria criteria = session.createCriteria(Goods.class);
Criteria type = criteria.createCriteria("type");
Criteria supplier= criteria.createCriteria("supplier");
if(null!=query.getGoodsname() && !"".equels(query.getGoodsname() ))
criteria.add(Restrictions.like("goodsname","%"+query.getGoodsname()+"%"));
if(null!=query.getSuppliername() && !"".equels(query.getSuppliername() ))
supplier.add(Restrictions.like("suppliername","%"+query.getSuppliername()+"%"));
if(null!=query.getTypename() && !"".equels(query.getTypename() ))
type.add(Restrictions.like("typename","%"+query.getTypename+"%"));
List list = criteria.list();
session.clear();
session.close();
return list;
}
以上斚wq可多层的嵌套,如type里还有外键,可以按照以上Ҏ(gu)q行嵌套。注意,查询时所有涉及到的数据都一ơ性写入类的属性中Q包括有兌的,x时goods的关联gq加载无效,我觉得这一炚w常的好。呵呵,有什么好处,可以自己好好的想惟?br /> 有许多h曄提到q用ExampleQ就不用自己判断了,如果没有兌条g查询的话Q确实是好,可它的缺点就是不能查询关联中的条件?
设计上可以灵zȝҎ(gu) Criteria 的特Ҏ(gu)方便地进行查询条件的l装。现在对 Hibernate的Criteria 的用法进行ȝQ?br />
Hibernate 设计?CriteriaSpecification 作ؓ Criteria 的父接口Q下面提供了 Criteria和DetachedCriteria ?
Criteria ?DetachedCriteria 的主要区别在于创建的形式不一P Criteria 是在U的Q所以它是由 Hibernate Session q行创徏的;?DetachedCriteria 是离U的Q创建时无需 SessionQDetachedCriteria 提供?2 个静态方?forClass(Class) ?forEntityName(Name) q行DetachedCriteria 实例的创建?Spring 的框架提供了getHibernateTemplate().findByCriteria(detachedCriteria) Ҏ(gu)可以很方便地Ҏ(gu)DetachedCriteria 来返回查询结
果?
Criteria ?DetachedCriteria 均可使用 Criterion ?Projection 讄查询条g。可以设|?FetchMode( 联合查询抓取的模?) Q设|排序方式。对?Criteria q可以设|?FlushModel Q冲?Session 的方式)?LockMode Q数据库锁模式)?
下面?Criterion ?Projection q行详细说明?br />
Criterion ?Criteria 的查询条件。Criteria 提供?add(Criterion criterion) Ҏ(gu)来添加查询条件?br />
Criterion 接口的主要实现包括: Example ?Junction ?SimpleExpression 。?Junction 的实际用是它的两个子类 conjunction ?disjunction Q分别是使用 AND ?OR 操作W进行来联结查询条g集合?br />
Criterion 的实例可以通过 Restrictions 工具cL创徏QRestrictions 提供了大量的静态方法,?eq Q等于)?ge Q大于等于)?between {来Ҏ(gu)的创?Criterion 查询条g QSimpleExpression 实例Q。除此之外, Restrictions q提供了Ҏ(gu)来创?conjunction ?disjunction 实例Q通过往该实例的 add(Criteria) Ҏ(gu)来增加查询条件Ş成一个查询条仉合?br />
至于 Example 的创建有所不同Q?Example 本n提供了一个静态方?create(Object entity) Q即Ҏ(gu)一个对象(实际使用中一般是映射到数据库的对象)来创建。然后可以设|一些过滤条Ӟ
Example exampleUser =Example.create(u)
.ignoreCase() // 忽略大小?
.enableLike(MatchMode.ANYWHERE);
/ / ?String cd的属性,无论在那里值在那里都匹配。相当于 %value%
Project 主要是让 Criteria 能够q行报表查询Qƈ可以实现分组?Project 主要?SimpleProjection ?ProjectionList ?Property 三个实现。其?SimpleProjection ?
ProjectionList 的实例化是由内徏?Projections 来完成,如提供的 avg ?count ?max ?min ?sum 可以让开发者很Ҏ(gu)Ҏ(gu)个字D进行统计查询?
Property 是对某个字段q行查询条g的设|,如通过Porperty.forName(“color”).in (new String[]{“black”,”red”,”write”}); 则可以创Z?Project 实例。通过
criteria ?add(Project) Ҏ(gu)加入到查询条件中厅R?
使用 Criteria q行查询Q主要要清晰的是 Hibernate 提供了那些类和方法来满开发中查询条g的创建和l装Q下面介l几U用法:
1. 创徏一个Criteria 实例
org.hibernate.Criteria接口表示特定持久cȝ一个查询。Session?Criteria实例的工厂?br />
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
2. 限制l果集内?br /> 一个单独的查询条g是org.hibernate.criterion.Criterion 接口的一个实例?/p>
org.hibernate.criterion.Restrictionsc?定义了获得某些内|Criterioncd的工厂方法?br />
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();
U束可以按逻辑分组?
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
.add( Restrictions.disjunction()
.add( Restrictions.isNull("age") )
.add( Restrictions.eq("age", new Integer(0) ) )
.add( Restrictions.eq("age", new Integer(1) ) )
.add( Restrictions.eq("age", new Integer(2) ) )
) )
.list();
Hibernate提供了相当多的内|criterioncd(Restrictions 子类), 但是其有用的是可以允许
你直接用SQL?/p>
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%",
Hibernate.STRING) )
.list();
{alias}占位W应当被替换查询实体的列别名?
Property实例是获得一个条件的另外一U途径。你可以通过调用Property.forName() 创徏一?/p>
Property?
Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.disjunction()
.add( age.isNull() )
.add( age.eq( new Integer(0) ) )
.add( age.eq( new Integer(1) ) )
.add( age.eq( new Integer(2) ) )
) )
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
.list();
3. l果集排?br /> 你可以用org.hibernate.criterion.Order来ؓ查询l果排序?
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
List cats = sess.createCriteria(Cat.class)
.add( Property.forName("name").like("F%") )
.addOrder( Property.forName("name").asc() )
.addOrder( Property.forName("age").desc() )
.setMaxResults(50)
.list();
4. 兌
你可以用createCriteria()非常Ҏ(gu)的在互相兌的实体间建立 U束?/p>
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.createCriteria("kittens")
.add( Restrictions.like("name", "F%")
.list();
注意W二?createCriteria()q回一个新?Criteria实例Q该实例引用kittens 集合中的元素?
接下来,替换形态在某些情况下也是很有用的?/p>
List cats = sess.createCriteria(Cat.class)
.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();
(createAlias()q不创徏一个新?Criteria实例?
Cat实例所保存的之前两ơ查询所q回的kittens集合?没有被条仉qo的。如果你希望只获?/p>
W合条g的kittensQ?你必M用returnMaps()?
List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.returnMaps()
.list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
Cat kitten = (Cat) map.get("kt");
}
5. 动态关联抓?br /> 你可以用setFetchMode()在运行时定义动态关联抓取的语义?
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();
q个查询可以通过外连接抓取mate和kittens?/p>
6. 查询CZ
org.hibernate.criterion.Examplecd怽通过一个给定实?构徏一个条件查询?/p>
Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();
版本属性、标识符和关联被忽略。默认情况下gؓnull的属性将被排除?
可以自行调整Example使之更实用?
Example example = Example.create(cat)
.excludeZeroes() //exclude zero valued properties
.excludeProperty("color") //exclude the property named "color"
.ignoreCase() //perform case insensitive string comparisons
.enableLike(); //use like for string comparisons
List results = session.createCriteria(Cat.class)
.add(example)
.list();
甚至可以使用examples在关联对象上攄条g?/p>
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();
7. 投媄(Projections)、聚合(aggregationQ和分组QgroupingQ?br />
org.hibernate.criterion.Projections?Projection 的实例工厂。我们通过调用
setProjection()应用投媄C个查询?
List results = session.createCriteria(Cat.class)
.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount() )
.add( Projections.avg("weight") )
.add( Projections.max("weight") )
.add( Projections.groupProperty("color") )
)
.list();
在一个条件查询中没有必要昑ּ的?"group by" 。某些投q型就是被定义?分组投媄Q他
们也出现在SQL的group by子句中?
可以选择把一个别名指z一个投影,q样可以使投影DU束或排序所引用。下面是两种不同?/p>
实现方式Q?/p>
List results = session.createCriteria(Cat.class)
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
.addOrder( Order.asc("colr") )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();
alias()和as()Ҏ(gu)便的一个投影实例包装到另外一?别名的Projection实例中。简而言之,
当你d一个投影到一个投影列表中?你可以ؓ它指定一个别名:
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount(), "catCountByColor" )
.add( Projections.avg("weight"), "avgWeight" )
.add( Projections.max("weight"), "maxWeight" )
.add( Projections.groupProperty("color"), "color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();
List results = session.createCriteria(Domestic.class, "cat")
.createAlias("kittens", "kit")
.setProjection( Projections.projectionList()
.add( Projections.property("cat.name"), "catName" )
.add( Projections.property("kit.name"), "kitName" )
)
.addOrder( Order.asc("catName") )
.addOrder( Order.asc("kitName") )
.list();
也可以用Property.forName()来表C投影:
List results = session.createCriteria(Cat.class)
.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount().as("catCountByColor") )
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();
8. ȝ(detached)查询和子查询
DetachedCriteriacM你在一个session范围之外创徏一个查询,q且可以使用L?Session?/p>
执行它?/p>
DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
.add( Property.forName("sex").eq('F') );
//创徏一个Session
Session session = .;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();
DetachedCriteria也可以用以表C子查询。条件实例包含子查询可以通过 Subqueries或?br />
Property获得?/p>
DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight).gt(avgWeight) )
.list();
DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();
怺兌的子查询也是有可能的Q?/p>
DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
.setProjection( Property.forName("weight").avg() )
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
.add( Property.forName("weight).gt(avgWeightForSex) )
.list();
我遇到到是第三种
解决hibernate 删除异常Q?deleted object would be re-saved by cascade (remove deleted object from associations) 收藏
在hibernate 删除兌时会出现eleted object would be re-saved by
cascade (remove deleted object from
associations)的异常,l合别h的和自己的经验通常有三U解决的Ҏ(gu)Q?/font>
Ҏ(gu)1 删除Set方的cascadeQ?br /> Ҏ(gu)2 解决兌关系后,再删?/font>
onside.getManys().remove(thisMany); //在所兌的一方的set中移走当前要删除的对?br />
thisMany.setOne(null); //讄所对应的一方ؓI,解除它们之间的关p?br />
manyDao.delete(thisMany);
Ҏ(gu)3 在many-to-one方增加cascade 但g能是none
? 于lazy机制Q?/font>
延迟初始化错误是q用Hibernate开发项目时最常见的错误。如果对一个类或者集合配|了延迟 索策略,那么必须当代理类实例或代理集合处于持久化状态(卛_于Session范围内)Ӟ才能初始化它。如果在游离状态时才初始化它,׃产生延迟初始 化错误?/font>
下面把Customer.hbm.xml文g?lt;class>元素的lazy属性设为trueQ表CZ用gq检索策略:
<class name="mypack.Customer" table="CUSTOMERS" lazy="true">
当执行Session的load()Ҏ(gu)ӞHibernate不会立即执行查询CUSTOMERS表的select语句Q仅仅返? Customercȝ代理cȝ实例Q这个代理类L(fng)以下特征Q?/font>
Q?Q? 由Hibernate在运行时动态生成,它扩展了Customerc,因此它承了Customercȝ所有属性和Ҏ(gu)Q但它的实现对于应用E序是透明 的?br /> Q?Q? 当Hibernate创徏Customer代理cd例时Q仅仅初始化了它的OID属性,其他属性都为nullQ因此这个代理类实例占用的内存很?br /> Q?Q当应用E序W一ơ访问Customer代理cd例时Q例如调用customer.getXXX()或customer.setXXX()Ҏ(gu)Q, Hibernate会初始化代理cd例,在初始化q程中执行select语句Q真正从数据库中加蝲Customer对象的所有数据。但有个例外Q那是? 应用E序讉KCustomer代理cd例的getId()Ҏ(gu)ӞHibernate不会初始化代理类实例Q因为在创徏代理cd例时OID存在了Q不? 到数据库中去查询?/font>
提示QHibernate采用CGLIB工具来生成持久化cȝ代理cRCGLIB是一个功能强大的Java字节码生成工P它能够在E? 序运行时动态生成扩? JavacL者实现Java接口的代理类。关于CGLIB的更多知识,请参考:
http://cglib.sourceforge.net/?/font>
以下代码先通过Session的load()Ҏ(gu)加蝲Customer对象Q然后访问它的name属性:
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getName();
tx.commit();
在运行session.load()Ҏ(gu)时Hibernate不执行Q何select语句Q仅仅返回Customercȝ代理cȝ? 例,它的OID?Q这是由load()Ҏ(gu)的第二个参数指定的。当应用E序调用customer.getName()Ҏ(gu)ӞHibernate会初? 化Customer代理cd例,从数据库中加载Customer对象的数据,执行以下select语句Q?/font>
select * from CUSTOMERS where ID=1;
select * from ORDERS where CUSTOMER_ID=1;
?lt;class>元素的lazy属性ؓtrueQ会影响Session的load()Ҏ(gu)的各U运行时行ؓQ下面D例说 明?/font>
1Q如果加载的Customer对象在数据库中不存在QSession的load()Ҏ(gu)不会抛出异常Q只有当q行 customer.getName()Ҏ(gu)时才会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.ObjectNotFoundException: No row with the given
identifier exists: 1, of class:
mypack.Customer
2Q如果在整个Session范围内,应用E序没有讉KqCustomer对象Q那么Customer代理cȝ实例一直不会被初始 化,Hibernate不会执行Mselect语句。以下代码试囑֜关闭Session后访问Customer游离对象Q?/font>
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
tx.commit();
session.close();
customer.getName();
׃引用变量customer引用的Customer代理cȝ实例在Session范围内始l没有被初始化,因此在执? customer.getName()Ҏ(gu)ӞHibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the
owning Session was closed
由此可见QCustomer代理cȝ实例只有在当前Session范围内才能被初始化?/font>
3Qnet.sf.hibernate.Hibernatecȝinitialize()静态方法用于在Session范围内显式初? 化代理类实例QisInitialized()Ҏ(gu)用于判断代理cd例是否已l被初始化。例如:
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
if(!Hibernate.isInitialized(customer))
Hibernate.initialize(customer);
tx.commit();
session.close();
customer.getName();
以上代码在Session范围内通过Hibernatecȝinitialize()Ҏ(gu)昑ּ初始化了Customer代理cd例,? 此当Session关闭后,可以正常讉KCustomer游离对象?/font>
4Q当应用E序讉K代理cd例的getId()Ҏ(gu)Ӟ不会触发Hibernate初始化代理类实例的行为,例如Q?/font>
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getId();
tx.commit();
session.close();
customer.getName();
当应用程序访问customer.getId()Ҏ(gu)Ӟ该方法直接返回Customer代理cd例的OID|无需查询数据库。由? 引用变量 customer始终引用的是没有被初始化的Customer代理cd例,因此当Session关闭后再执行customer.getName()Ҏ(gu)Q? Hibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the
owning Session was closed
解决Ҏ(gu)Q?/p>
׃hibernate采用了lazy=true,q样当你用hibernate查询?q回实际为利用cglib增强的代理类,但其q没有实际填
?当你在前?利用它来取?getXXX)?q时Hibernate才会到数据库执行查询,q填充对?但此时如果和q个代理cȝ关的
session已关闭掉,׃产生U错?
在做一对多Ӟ有时会出?could not initialize proxy - clothe owning Session was
sed,q个好像是hibernate的缓存问?问题解决:需要在<many-to-one>里设|lazy="false".
但有可能会引发另一个异常叫
failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed
此异常解x案请察看本h博客Qhttp://hi.baidu.com/kekemao1Q的Hibernate异常中的《failed to lazily initialize a collection of role异常?/p>
?
解决Ҏ(gu):在web.xml中加?br />
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
可以了;
参考了:
Hibernate与gq加载:
Hibernate对象关系映射提供延迟的与非gq的对象初始化。非延迟加蝲在读取一个对象的时候会与q个对象所有相关的其他对象一赯取出来? q有时会D成百的(如果不是成千的话Qselect语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,l常会导致整个数据库都在初始? 的阶D被d来了。当Ӟ你可以不厌其烦地查每一个对象与其他对象的关p,q把那些最昂贵的删除,但是到最后,我们可能会因此失M本想在ORM工具? 获得的便利?/p>
一个明昄解决Ҏ(gu)是用Hibernate提供的gq加载机制。这U初始化{略只在一个对象调用它的一对多或多对多关系时才关pd象读取出来。这个过
E对开发者来说是透明的,而且只进行了很少的数据库操作hQ因此会得到比较明显的性能提升。这Ҏ(gu)术的一个缺h延迟加蝲技术要求一?
Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式持久层抽象出来时的一个主要问题。ؓ了将持久化机制完全地抽象出来Q所
有的数据库逻辑Q包括打开或关闭会话,都不能在应用层出现。最常见的是Q一些实C单接口的DAO实现cd数据库逻辑完全装h了。一U快速但是笨?
的解x法是攑ּDAO模式Q将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系l中Q这是一个严重的设计~陷Q妨了pȝ?
可扩展性?/p>
在Web层进行gq加?/p>
q运的是QSpring框架为Hibernate延迟加蝲与DAO模式的整合提供了一U方便的解决Ҏ(gu)。对那些不熟(zhn)Spring? Hibernate集成使用的hQ我不会在这里讨多的l节Q但是我你去了解Hibernate与Spring集成的数据访问。以一个Web应用? 例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择 一个类来实现相同的功能。两U方法唯一的不同就在于interceptor在Spring容器中运行ƈ被配|在web应用的上下文中,而Filter? Spring之前q行q被配置在web.xml中。不用哪个Q他们都在请求将当前会话与当前(数据库)U程l定时打开Hibernate会话。一旦已l? 定到U程Q这个打开了的Hibernate会话可以在DAO实现cM透明C用。这个会话会为gq加载数据库中值对象的视图保持打开状态。一旦这个逻辑? 囑֮成了QHibernate会话会在Filter的doFilterҎ(gu)或者Interceptor的postHandleҎ(gu)中被关闭。下面是每个l? 件的配置CZQ?/p>
Interceptor的配|?
<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
</bean>
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref
bean="sessionFactory"/></property>
</bean>
</beans>
Filter的配|?/p>
<web-app>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*. spring </url-pattern>
</filter-mapping>
</web-app>
实现Hibernate的Dao接口来用打开的会话是很容易的。事实上Q如果你已经使用了Spring框架来实C的Hibernate
Dao,很可能你不需要改变Q何东ѝ方便的HibernateTemplate公用lg使访问数据库变成菜一,而DAO接口只有通过q个lg才可?
讉K到数据库。下面是一个示例的DAOQ?/p>
public class HibernateProductDAO extends HibernateDaoSupport implements
ProductDAO {
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
在业务逻辑层中使用延迟加蝲
即在视囑֤面,Spring框架也通过使用AOP 拦截? HibernateInterceptor来得gq加载变得很Ҏ(gu)实现。这个Hibernate 拦截器透明地将调用配置在Spring应用E序上下文中的业务对象中Ҏ(gu)的请求拦截下来,在调用方法之前打开一个Hibernate会话Q然后在Ҏ(gu)执行 完之后将会话关闭。让我们来看一个简单的例子Q假设我们有一个接口BussinessObjectQ?/p>
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
cBusinessObjectImpl实现了BusinessObject接口:
public class BusinessObjectImpl implements
BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
通过在Spring应用E序上下文中的一些配|,我们可以让将调用BusinessObject的方法拦截下来,再o它的Ҏ(gu)支持延迟加蝲。看看下 面的一个程序片D:
<beans>
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget"
class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref
bean="someDAO"/></property>
</bean>
<bean id="businessObject"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref
bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>
当businessObject被调用的时候,HibernateInterceptor打开一个Hibernate会话Qƈ调用请求传递给 BusinessObjectImpl对象。当BusinessObjectImpl执行完成后,HibernateInterceptor透明地关闭了 会话。应用层的代码不用了解Q何持久层逻辑Q还是实C延迟加蝲?/p>
在单元测试中试延迟加蝲
最后,我们需要用J-Unit来测试我们的延迟加蝲E序。我们可以轻易地通过重写TestCasecM的setUp和tearDownҎ(gu)来实现这 个要求。我比较喜欢用这个方便的抽象cM为我所有测试类的基cR?/p>
public abstract class MyLazyTestCase extends TestCase {
private SessionFactory sessionFactory;
private Session session;
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory)
getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new
SessionHolder(s));
}
protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}
public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder)
TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
Java数据cd | Hibernate数据cd | 标准SQL数据cd (PS:对于不同的DB可能有所差异) |
byte、java.lang.Byte | byte | TINYINT |
short、java.lang.Short | short | SMALLINT |
int、java.lang.Integer | integer | INGEGER |
long、java.lang.Long | long | BIGINT |
float、java.lang.Float | float | FLOAT |
double、java.lang.Double | double | DOUBLE |
java.math.BigDecimal | big_decimal | NUMERIC |
char、java.lang.Character | character | CHAR(1) |
boolean、java.lang.Boolean | boolean | BIT |
java.lang.String | string | VARCHAR |
boolean、java.lang.Boolean | yes_no | CHAR(1)('Y'?N') |
boolean、java.lang.Boolean | true_false | CHAR(1)('Y'?N') |
java.util.Date、java.sql.Date | date | DATE |
java.util.Date、java.sql.Time | time | TIME |
java.util.Date、java.sql.Timestamp | timestamp | TIMESTAMP |
java.util.Calendar | calendar | TIMESTAMP |
java.util.Calendar | calendar_date | DATE |
byte[] | binary | VARBINARY、BLOB |
java.lang.String | text | CLOB |
java.io.Serializable | serializable | VARBINARY、BLOB |
java.sql.Clob | clob | CLOB |
java.sql.Blob | blob | BLOB |
java.lang.Class | class | VARCHAR |
java.util.Locale | locale | VARCHAR |
java.util.TimeZone | timezone | VARCHAR |
java.util.Currency | currency | VARCHAR |
name: cd |
2.id节点
1.column 字段名称 |
3.property 节点
1.column 数据库表字段名称 |