
2

3


5

6

7


9

10

11

12

13

14


16

17

18

19

20

21



24

25

26

27

28

29

30


32

33

34

35


37

38

39

40

41

hibernate的inverse?manyQtoQmany 关系谈
Inverse是hibernate双向关系中的基本概念Q当然对于多数实体,我们q不需要双向关联,更多的可能会选择单向兌Q况且我们大多数Z般采用一对多关系Q而一对多双向兌的另一端:多对一的inverse属性是不存在,其实它默认就是inverse=false.从而防止了在一对多端胡p|inverse也不至于出错。但是inverse讄不当实会带来很大的性能影响Q这Ҏ(gu)我们必须x的?/p>
q篇文章已经详细分析了inverse讄不当带来的媄响:
http://www.hibernate.org/155.html
看了q篇文章Q还是很有必要再写下一些ȝ的:
1Qinverse中提及的side其实是指一个类或者表的概念,双向兌其实是指双方都可以取得对方的应用?/p>
2Q维护关p这个名词还是稍显模p或者晦涩。我们一般说AcL者A表(q里的表的是指多对多的连接表Q有责Ql护关系Q其实这里的意思是_我在应用在更斎ͼ创徏Q删除(d不用说了Q双向引用正是ؓ了方便读而出玎ͼAcL者A表时Q此时创建的SQL语句必须有责M证关pȝ正确修改?/p>
3Qinverse=false的sideQside其实是指inverseQfalse所位于的class元素Q端有责ȝ护关p,而inverseQtrue端无ȝ护这些关pR?/p>
4Q我们说inverse讄不当会导致性能低下Q其实是说inverse讄不当Q会产生多余重复的SQL语句甚至致JDBC exception的throw。这是我们在建立实体cdpL必须需要关注的地方。一般来_inverseQtrue是推荐用,双向兌中双斚w讄inverseQfalse的话Q必会导致双斚w重复更新同一个关pR但是如果双斚w讄inverseQtrue的话Q双斚w不维护关pȝ更新Q这也是不行的,好在一对多中的一?many-to-one默认是inverseQfalseQ避免了q种错误的生。但是对多对没有这个默认设|了Q所以很多hl常在多对多的两端都使用inverseQtrueQ结果导致连接表的数据根本没有记录,是因ؓ他们双分都没有责ȝ护关pR所以说Q双向关联中最好的讄是一端ؓinverseQtrueQ一端ؓinverseQfalse。一般inverseQfalse会放在多的一端,那么有h提问了,manyQtoQmany两边都是多的Qinverse到底攑֜哪儿Q其实hibernate建立多对多关pM是将他们分离成两个一对多关系Q中间连接一个连接表。所以通用存在一对多的关p,也可以这栯Q一对多是多对多的基本组成部分?/p>
看下面的多对多的定义大家更会清楚”多对多“与“一对多”的关系Q其中我们注?lt;many-to-many />标签的特点就知道Q它是定义了一个多对多关系Q而不?lt;one-to-many/>?/p>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="org.hibernate.auction">
<class name="TestA" table="TestA"
dynamic-update="true" dynamic-insert="true" >
<id name="id" column="id" type="int" unsaved-value="any" >
<generator class="assigned">
</generator>
</id>
<property name="name" type="java.lang.String"
update="true" insert="true" column="name" />
<set name="testBs" table="TestA_TestB" inverse="false" cascade="all">
<key column="testA"/>
<many-to-many column="testB" class="TestB" />
</set>
</class>
<class name="TestB" table="TestB"
dynamic-update="true" dynamic-insert="true" >
<id name="id" column="id" type="int" unsaved-value="any" >
<generator class="assigned">
</generator>
</id>
<property name="name" type="java.lang.String" update="true"
insert="true" column="name" />
<set name="testAs" table="TestA_TestB" inverse="true" cascade="all">
<key column="testB"/>
<many-to-many column="testA" class="TestA" />
</set>
</class>
</hibernate-mapping>
在对多对中,因ؓ一端维护关pd一端不l护关系的原因,我们必须注意避免在应用中用不l护关系的类建立关系Q因样徏立的关系是不会在数据库中存储的。基于上面的映射文g代码l出一个例子:
package org.hibernate.auction;
import java.util.*;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class TestA {
int id;
String name;
Set testBs=new HashSet();
public TestA(){
}
public TestA(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestBs(){
return testBs;
}
public void setTestBs(Set s){
testBs=s;
}
public void addTestB(TestB tb){
testBs.add(tb);
}
public static void main(String[] args) {
}
}
public class TestB {
int id;
String name;
Set testAs=new HashSet();
public TestB(){
}
public TestB(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestAs(){
return testAs;
}
public void setTestAs(Set s){
testAs=s;
}
public void addTestA(TestA ta){
testAs.add(ta);
}
public static void main(String[] args) {
}
}
试代码Q?/p>
public void doTest() throws Exception{
TestA a1=new TestA(1);
TestA a2=new TestA(2);
TestA a3=new TestA(3);
TestB b1=new TestB(1);
TestB b2=new TestB(2);
TestB b3=new TestB(3);
a1.addTestB(b1);
a1.addTestB(b2);
a1.addTestB(b3);
b1.addTestA(a1);
b2.addTestA(a2);
Session s = factory.openSession();
s = factory.openSession();
Session session = factory.openSession();
session.save(a1);
session.flush();
session.close();
}
试后连接表的数据ؓQ?/p>
testa testb
1 1
1 2
1 3
Ҏ(gu)inverse规则Q对q些代码Qb2.addTestA(a1); b2.addTestA(a2); 建立的关p,数据库ƈ没有存储下来Q因为TestB没有责Ql护q些关系Q所以生的sql语句自然不会有针对Testa_testB表的操作了。假讑ֺ用中真的需要这些方法,那么我们可以修改TestB的方法,让他们注意在l护端类中执行相应的操作以得关p能够在数据库中保存下来Q更改TestB如下Q?/p>
/*
* Created on 2004-7-25
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package org.hibernate.auction;
import java.util.*;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class TestB {
int id;
String name;
Set testAs=new HashSet();
public TestB(){
}
public TestB(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestAs(){
return testAs;
}
public void setTestAs(Set s){
testAs=s;
}
public void addTestA(TestA ta){
testAs.add(ta);
ta.addTestB(this);
}
public static void main(String[] args) {
}
}
那么试执行后连接表的数据ؓQ?/p>
testa testb
1 2
1 3
1 1
2 2
试通过?br />
对原生SQL查询执行的控制是通过SQLQuery接口q行的,通过执行Session.createSQLQuery()获取q个接口。最单的情况下,我们可以采用以下形式Q?/font>
List cats = sess.createSQLQuery("select * from cats")
.addEntity(Cat.class)
.list();
q个查询指定?
SQL查询字符?/font>
查询q回的实?/font>
q里Q结果集字段名被假设Z映射文g中指明的字段名相同。对于连接了多个表的查询Q这可能造成问题Q因为可能在多个表中出现同样名字的字Dc下面的Ҏ(gu)可以避免字D名重复的问?
List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.list();
q个查询指定?
SQL查询语句Q它带一个占位符Q可以让Hibernate使用字段的别?
查询q回的实体,和它的SQL表的别名.
addEntity()Ҏ(gu)SQL表的别名和实体类联系hQƈ且确定查询结果集的Ş态?
addJoin()Ҏ(gu)可以被用于蝲入其他的实体和集合的兌.
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens")
.list();
原生的SQL查询可能q回一个简单的标量值或者一个标量和实体的结合体?
Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();
除此之外Q你q可以在你的hbm文g中描q结果集映射信息Q在查询中用?/font>
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();
可以在映文档中定义查询的名?然后可以象调用一个命名的HQL查询一L接调用命名SQL查询.在这U情况下,我们?/em> 需要调?tt class="literal">addEntity()Ҏ(gu).
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>
List people = sess.getNamedQuery("persons")q里要特别的提醒一下大家千万不要把主键忘了。刚开始我忘了主键,后来调试了半天才扑և原因来?br />
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();
我觉得这U发式比较好。这样写出来的sql可以很整齐。我们的数据库用的是oracleQ不q按照这上面的写法发现sql语句有错误?br />后来拿到控制台执行也抱错。因为原来都用sqlserverQ而sqlserver都是可以的。后来发现是表不能有别名Ҏ(gu)q样好了?br />SELECT T_PAY.sys_id as {pay.sysId},
T_PAY.sys_flag as {pay.sysFlag},
T_PAY.sys_domain as {pay.sysDomain},
T_PAY.sys_owner as {pay.sysOwner},
T_PAY.sys_create_date as {pay.sysCreateDate},
T_PAY.sys_update_date as {pay.sysUpdateDate},
T_PAY.pay_id as {pay.payId},
T_PAY.pay_name as {pay.payName},
T_PAY.pay_type_id as {pay.payTypeId},
T_PAY.pay_date as {pay.payDate},
T_PAY.money_type_id as {pay.moneyTypeId},
T_PAY.amount as {pay.amount},
T_PAY.payer_id as {pay.payerId},
T_PAY.payer_name as {pay.payerName},
T_PAY.accept_id as {pay.acceptId},
T_PAY.accept_name as {pay.acceptName},
T_PAY.pay_state_id as {pay.payStateId},
T_PAY.remark as {pay.remark}
FROM T_PAY
JOIN T_BIZ_PAY
ON T_PAY.pay_id = T_BIZ_PAY.pay_id
WHERE T_BIZ_PAY.biz_id = :bizId
q两个星期以来,我把原来用struts开发的一个测试工h用struts+hibernate来实玎ͼ首先从心情上来,整个开发过E中始终保持愉快和^和,“原来开发可以这h快?”,再一点就是开发效率上高效了许多?br /> 现在sun又加入jdocentral.com开始着手JDO2.0Q想想看{它出台以后是一个怎样Ȁ动h心得场面Q让我们拭目以待?br />
|
q两天来在研IHibernate中的one-to-one和one-to-many的问题,做了很多试验Q发现实现表关系Ӟ在hibernate-mapping中generator的class设ؓuuid.hexQ上q的两种关系可以实现Q也是数据库的主键设ؓvarcharQMSSQL2000Q,但是主键Ҏ(gu)int型的标识却弹出“ERROR SessionImpl:2379 - Could not synchronize database state with session”和“net.sf.hibernate.HibernateException: SQL insert, update or delete failed (row not found)”异常,百思不得其解。下面脓(chung)Z键设为uuid.hex时的实现代码Q?br /> package test.pojo; public class Child { public String getCid() { public void setCid(String cid) { public String getCname() { public void setCname(String cname) { public String getPid() { public void setPid(String pid) { public Parent getParent() { public void setParent(Parent parent) { import java.util.HashSet;
public class Parent {
public String getPid() {
public void setPid(String pid) {
public String getPname() {
public void setPname(String pname) {
public java.util.Set getChild() {
public void setChild(java.util.Set child) {
}
public class TestHibernate {
public static void main(String[] argv) { |
很多人都对Java在批量数据的处理斚w是否是其合适的场所持有怀疑的念头Q由此g伸,那么׃认ؓORM可能也不是特别适合数据的批量处理?其实Q我惛_果我们应用得当的话,完全可以消除ORM扚w处理性能问题q方面的虑。下面以HibernateZ来做明,假如我们真的不得不在Java中用Hibernate来对数据q行扚w处理的话?向数据库插入100 000条数据,用Hibernate可能像这P
大概在运行到W?0 000条的时候,׃出现内存溢出而失败。这是Hibernate把最q插入的Customer都以session-level cache在内存做~存Q我们不要忘记Hiberanteq没有限制first-level cache 的缓存大: # 持久对象实例被管理在事务l束Ӟ此时Hibernate与数据库同步M已经发生?化的被管理的的对象?br /> # Session实现了异步write-behindQ它允许Hibernate昑ּ地写操作的批处理?q里Q我l出Hibernate如何实现扚w插入的方法: 首先Q我们设|一个合理的JDBC批处理大,hibernate.jdbc.batch_size 20?然后在一定间隔对Sessionq行flush()和clear()?br />
那么Q关于怎样删除和更新数据呢Q那好,在Hibernate2.1.6或者更后版本,scroll() q个Ҏ(gu)是最好的途径Q?br />
q种做法q不困难Q也不算不优雅。请注意Q如果Customer启用了second-level caching Q我们仍然会有一些内存管理的问题。原因就是对于用L每一ơ插入和更新QHibernate在事务处理结束后不得不通告second-level cache 。因此,我们在批处理情况下将要禁用用户用缓存?br /> |
性能试Q?br /> 对一?036条记录的表进行load试Q表字段?个,有一个字D|text型,每条记录的该字段U有1300多字节,其余都是单字D?br /> 软硬ӞAMD XP1600+, 512M内存Q测试时内存有余Q,winXP SP1, JDK1.4.2_06, Mysql4.1.9,InnoDB,GBK, mm.3.1.6驱动Q?tomcat5.0.28Q启动设|最大内?56MQ最内?4MQ?hibernate2.1.8, 打开二~存EhcacheQ配|二U缓存最?0000个对象)关闭所有hibernate log信息和sql输出
注意事项Q?br /> (1)不要在机器运行较长时间后q行tomcatq行试Q特别是机器启动后频J地开启tomcatQjavaE序Q打开较大应用E序占用很多内存的时候。那时候测试结果一定不准。比如:我昨天晚上测试先用方?的时间是200SQ?0SQ后q行Ҏ(gu)2Q竟然用?000S,200S! |