引用: |
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory |
引用: |
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory |
引用: |
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory |
java代码: |
Session session = sf.openSession(); Transaction tx = session.beginTransactioin(); ... session.flush(); tx.commit(); session.close(); |
java代码: |
net.sf.hibernate.transaction.JDBCTransaction: public void begin() throws HibernateException { log.debug("begin"); try { toggleAutoCommit = session.connection().getAutoCommit(); if (toggleAutoCommit) session.connection().setAutoCommit(false); } catch (SQLException e) { log.error("Begin failed", e); throw new TransactionException("Begin failed with SQL exception: ", e); } begun = true; } |
java代码: |
public void commit() throws HibernateException { if (!begun) throw new TransactionException("Transaction not successfully started"); log.debug("commit"); try { if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush(); try { session.connection().commit(); committed = true; } catch (SQLException e) { log.error("Commit failed", e); throw new TransactionException("Commit failed with SQL exception: ", e); } } finally { session.afterTransactionCompletion(); } toggleAutoCommit(); } |
java代码: |
Connection conn = ...; <--- session = sf.openSession(); conn.setAutoCommit(false); <--- tx = session.beginTransactioin(); ... <--- ... conn.commit(); <--- tx.commit(); (对应左边的两?SPAN style="COLOR: #000000">) conn.setAutoCommit(true); conn.close(); <--- session.close(); |
java代码: |
javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction"); Session s1 = sf.openSession(); ... s1.flush(); s1.close(); ... Session s2 = sf.openSession(); ... s2.flush(); s2.close(); tx.commit(); |
java代码: |
public void begin(InitialContext context, ... ... ut = (UserTransaction) context.lookup(utName); ... |
java代码: |
public void commit() ... ... if (newTransaction) ut.commit(); ... |
java代码: |
|---CMT(Container Managed Transaction) | |---BMT(Bean Managed Transaction) | |----JDBC Transaction | |----JTA Transaction |
java代码: |
Session s = sf.openSession(); ...... s.close(); |
java代码: |
javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction"); Session s1 = sf.openSession(); ... s1.flush(); s1.close(); ... Session s2 = sf.openSession(); ... s2.flush(); s2.close(); tx.commit(); |
引用: |
但sf.opengSession()Ӟq没有setAutoCommit(false)Q我想问的是Q如果不~写M事务代码Q如Q? Session s = sf.openSession(); ...... s.close(); 数据库会不会有反应(此时应该是默认AutoCommit为trueQ?/TD> |
引用: |
另外Q我想问一下: 1. s.flush()是不是必ȝ 2. s.close()是不是一定要关闭 |
引用: |
s1不关闭,使用s2q行操作的代码中使用s1可不可以Q我觉得q样更加节约资源Q不需要反复的q接、关闭) |
java代码: |
Class A { find() { Session s1 = sf.openSession(); ... s1.flush(); s1.close(); } } |
java代码: |
Class B { find() { Session s2 = sf.openSession(); ... s2.flush(); s2.close(); } } |
java代码: |
Main { tx = ...; A.find(); B.find(); tx.commit(); } |
JDO之前世今?/B> | |
1 Java与数据库应用QJDBCJava发明以来Q在短短的几q之_q速占领了从桌面应用(J2SEQ到服务器(J2EEQ,再到型讑֤嵌入式系l(J2MEQ的应用开发市场,其语a吸取了SmallTalk的一切皆对象的理念,摆脱了C++的历史篏赘,z、自q风格赢得了很多开发者的喜爱。从JDK1.1开始,Java成ؓ实用的语aQ而不是被望的新品UQ再l过JDK1.2的大量增强(其是Collection FrameworkQ,JDK1.3的虚拟机效率提升QHotSpotQ,JDK1.4的融合百家之长(Logging、RegExp、NewIO{)Q现在已l是成熟E重Q颇昑֤安范?BR>在企业市场上,大部分的应用建立在数据库基础上,数据是企业的生命Q传l开发语aQ包括面向过E的C、面向对象的C++、变UPascal的DelphiQ非常棒的语aQ我用过四年Q,面向数据的PowerBuilder{等Q先后在数据库开发的舞台上展现风ѝJava当然不会放过q些Q于是,出现了JDBC。在JDBC的帮助下QJava也迅速渗入数据库开发的市场Q尤其是面向企业服务器的应用开发?BR>今天要谈的JDOQ与JDBC有非常密切的关系Q尽JDOq不是只面向JDBC的数据对象包装规范。下面先单地介绍一下JDBC?BR> 1.1 关系数据库之癑֮争鸣QODBC关系数据库的历史一a隑ְQ我只能从我的接触经历和所见所闻,单地叙述一下。最早的时候,计算只在一些大型的研究机关露面Qƈ不是普罗大众可以涉及的。苹果电脑将个h电脑引入民间Q再随着IBM的PC标准开放,个h电脑逐步普及开来,加上微Y的DOS操作pȝQ以及Borland的Turbopd语言开发环境,老百姓发现原来电脑可以做q么多事Q后来,出现了DBASEQ一个简单的关系数据库系l,和SQL语言。后来,Borland看到了数据库的市场前景,推出了ParadoxQ也是当今Delphi和C++Builder中仍然用的ParadoxQ,一丑֍领了民用数据库的大部分江山,之后QBorlandq脆收购了DbaseQ后来又购买了InterBaseQ将数据库市场的领先优势一直保持到Windows3.0出现。这时候,微Y在Windows1.0?.0被h痛骂之后强地推?.0Q以及更E_?.1和Win32APIQ造就了个人电脑桌面操作系l的怸CQ在Borland未警觉的情况下,购买了同样具有类Dbase数据库技术的Fox公司Qƈq速将其易用化QŞ成了FoxBaseQ后来演变成FoxProQ逐渐过了BorlandQ成Z人电脑数据库的大戗微软再接再励,为简单易用而低负荷要求的数据库应用开发了AccessQ赢得了q大开发h员的心。当Ӟ同期的Oracle、Sybase、Informix{商用数据库凭专注于企业U数据库技术成为高端的几位领军人物。微软当然也x为高端数据库供应商之一Q于是自行开发一套面向企业应用的数据库Q不q很快项目夭折,微Y不甘心,购买了Sybase的底层TDS技术,包装成了SQL ServerQ凭微Y的高度易用性的特点Q也占领了不市场?BR>当市Z出现众多的数据库产品之后QBorland和微软都发现自己拥有的数据库产品挺多Q市Z不小Q不同的产品l用户带来不同的配置dQ不利于所有品的推广Q于是,两者纷U开始制定数据库讉K的规范,微Y推出了ODBCQ其面向开发h员的亲和性,逐步获得了认可,同时QBorlandU集了IBM和Novell也推ZIDAPI数据库接口规范,也就是今天BDE的核心,不过后来Novell和IBM先后退出,只剩Borland独力支撑。不qBorland是一个技术实力雄厚的公司Q其技术一向领先于微YQBDE的性能比初期的ODBC不知道要好多倍,后来微Y偷师学艺Q把q接池等技术加到ODBC中,在Delphi3.0及其BDE在市Z风光无限的时候,逐步赶了上来q有过。直C天,BDE仍是Borland的品线上的数据库访问标准,而微软如果不是将ODBC和多数数据库的客L内嵌qWindows的话Q估计BDE仍是市场的赢家。不q,微Y是玩弄市场的老手Q通过Ҏ作系l的垄断Q其数据库品和ODBC标准l究占据了多数开发市场?BR>1.2 从optional pack到JDK的标准APIJava开始涉及数据库应用后,Sun极力制定Java的数据库规范QJDBC API是cMODBC一PҎ据库讉K的底层协议进行最基本的包装,然后形成一套统一的数据访问接口,数据库连接、SQL语句句柄、结果集Q都带有ODBC的媄子。以方便配置为目的,Sun极力推荐完全瘦客L的TYPE 4型JDBC驱动Q这是一个不需要安装数据库客户端的驱动规范Q是现在使用最多的。当ӞZ保持与旧的数据库兼容QJDBC规范中包括了专用于连接ODBC的TYPE 1驱动和需要安装数据库客户端的TYPE 2驱动Q以及可以由厂商在数据库服务端专门提供面向JDBC的服务的TYPE 3驱动?BR>JDBC最早出现时Q还不属于标准JDK的一部分Q而是作ؓ一个额外包提供下蝲。后来,随着Java~写的数据库应用的的增多Q和JDBC规范本n的逐渐成熟QJDBCl于成ؓJDK1.1的一部分?BR>JDBC目前最新的?.0版本Q还有正在讨Z?.0版本。实际上Q在开发中使用得最多的q是1.0中的APIQ?.0中主要增加了可双向滚动的l果集、更新批处理{提高可用性和性能的APIQ?.0主要增加了连接池、可更新的结果集{特性?.0在可管理性、连接池规范化等斚w再做改进?BR> 2 面向对象与数据库现在的程序员Q没有不知道面向对象的。作为接q真实客观世界的开发概念,面向对象使程序代码更易读、设计更合理。在普遍存在的数据库应用领域Q开发h员对面向对象的追求从未停止过。从八十q代开始,有很多公司和研I机构在q行着面向对象与数据库l合的研I?BR> 2.1 SmallTalk、C与C++、Delphi—Object Pascal、Java面向对象的语a最早有好几U雏形,IBM的SmallTalk是其中最为流行的Q在SmallTalk中,一切都是对象,一切都是类Q它面向对象的概念发挥C极致。面向对象的~程比v传统的面向过E的方式了一大步QZ认识刎ͼ原来软g可以q样写。不q,׃计算机基本结构与底层g体系和系lY件的限制QSmallTalkq不能在理想的性能前提下推q到普通的应用上,q一Ҏ旉制了SmallTalk的发展,接着QC语言的面向对象版C++出现了,׃使用C语言的h很多QC++很快成ؓ面向对象~程的主语a。不q,Z保证与C的兼容,C++保留了很多面向过E的痕迹Q比如恶心的指针、全局变量{等。Pascal的改q版Object Pascal相对来说安全许多Q后来Borlandq脆Object Pascal换了个名字,叫DelphiQ从此开创了一片面向对象编E的C界, Delphi的严谨语法和快速编译吸引了众多的应用开发者,加上Borland的完的VCLlg体系Q比起MFC来方便而容易,另外QDelphi完整的数据库lgQ也数据库开发变得简单而容易,Delphi再次成ؓ成熟的面向对象开发语a。微软当然不会放q这些,通过MFC内置到操作系l中Q微软的VC++也抢回一些市场。这也是Z么Delphi开发的应用E序~译后会比VC、VB开发的E序大的原因?BR>1995q_Sun的一个开发小l本来ؓ了小型嵌入式pȝ开发OAK语言Q结果无心插x成荫Q发展出了Java语言Q它是一个完全摆׃传统语言的各U负担的面向对象的语aQ当Ӟ也保留了一些非面向对象的核心(原始cdQ以保证速度。现在Java也ؓ最行的面向对象语a之一。当Ӟ微Y同样不会放过它,擅于模仿的微软立卛_Z个C#来与之竞争,q在C#中保留了一些变U的指针Q指代)以吸引传l的C开发者。关于这些语a的各自特点,q里׃一一赘述了?BR>2.2 数据库与数据对象?/H4>对象数据库就是采用全新的面向对象概念来设计数据库的全新数据库cd。在q方面,主要以一些大学研I机构进行设计和开发,有些也Ş成了产品Q不q由于市场方面的原因Q主要是关系数据库的Ҏ上手和市场绝寚w导地位)和ODBMS先天的一些弱点(比如查询引擎很难优化Q,使ODBMS没有象关pL据库那样行h?BR>不过对象数据库的对象化特点还是o人割舍不下,目前q是有一些很好的产品在市ZQ从商用的到免费的都用。目前在ODBMS领域占据领导C的是Versant、FastObjects和ObjectStore{几大厂商,q且Q市Z额也在逐步扩展。免费的产品包括C++~写的Ozone、纯Java的db4o{等。还有一些研I机构开发一些底层的面向对象数据库引擎,但只提供一些底层的APIQ不提供理斚w的功能,以及一些算法提供开攑ּ接口Q让厂商去选择和实现。比如美国威斯康新大学计机pL据库l的SHORE引擎Q就是一个非常出色的面向对象数据库引擎,现在q在U极的更CQ一些其它研I机构和数据库厂商采用它完成了自q特别的对象数据库Q比如专用于地理信息的数据库、专用于宇宙I间数据研究的数据库{等?BR>目前对象数据库最大的障碍是缺乏统一的规范,各个数据库厂商有各自的访问接口。对象数据库比v关系数据库来Q不只是基本的几U数据类型那么简单,它还涉及l承处理、多态等一大堆面向对象特征的实玎ͼ规范化道路当然困N重。这也是对象数据库无法普及的一个重要原因?BR>也有一些机构提Z一些徏议的规范Q比如制定Corba标准的OMG组的一个分lODMG提出的ODMG规范Q目前已l是3.0版本Q其中的OQL对象查询语言相当h吸引力。还有一些中立的机构提出了其它的一些标准化的对象访问APIQ也可算是面向对象数据库的规范之一。象前面提到的FastObjects和Ozone是W合ODMG3.0规范的?BR> 3 Java对象映射话说回来Q在一般的开发h员眼中,数据库就是指关系数据库,因此Q很多应用还是采用简单的JDBC来访问数据库。在开发的q程中,大家逐渐感觉到JDBC的局限性,比如调用复杂、容易生资源泄漏等{,与面向对象的Java语言有一D距,因此Q很多开发小l开始思考如何将应用中的数据q行对象化徏模,然后再想办法与JDBCl合hQ这是Java数据库开发中的层ZIL对象包装技术?BR> 3.1 对象包装技?/H4>传统包装思义Q就是最初出现的包装方式Q很多公叔Rl历q这一步,产生了很多风格各异的包装Ҏ。当ӞW者也有过q算丰富的尝试过E?BR>举例来说Q如果我们有一个用LQ?BR>public class User { public int userId; public String name; public java.util.Date birthday; } 我们可以其当作一个简单的数据c,然后写一些工h法来实现与JDBC的交互。这些方法,我们可以攑ֈ一个另外的工具cMQ也可以攑ֈUsercM作ؓ静态方法。这些方法包括简单的增、删、改、查Q以OracleZQ: public class User { public int userId; public String name; public java.util.Date birthday; public static User addUser(String name, Date birthday) throws SQLException { Connection conn = ? //获取一个JDBCq接 PreparedStatement ps = conn.prepareStatement(“…?; // 获取一个序列值来作ؓ用户标识 ResultSet rs = ps.executeQuery(); rs.next(); User user = new User(); user.userId = rs.getInt(1); //d序列gؓ新用h?BR> user.name = name; user.birthday = birthday; ps = conn.prepareStatement(“insert into ??; //插入用户数据记录的SQL ps.setInt(1,user.id); ps.setString(2,user.name); ps.setDate(3,user.birthday); ps.executeUpdate(); rs.close(); ps.close(); conn.close(); return user; } public static void deleteUser(int userId) throws SQLException { Connection conn = ?; //?BR>} public static User getById(int userId) throws SQLException { //?BR>} //?BR>} 以上是一个简单的数据包装的基本雏形,我们可以看到Q这是一个非常简单的JDBC包装Q一些代码可以模块化Q以实现重用。另外,q段代码q有很大隐患Q就是中途如果出现异常的话,׃使系l出现JDBC资源漏洞Q因为JDBC分配的资源(conn,ps,rs{)是不能被Java虚拟机的垃圾回收机制回收的。因此,我们的addUserҎ需要改成下面的样子Q?BR> public static User addUser(String name, Date birthday) throws SQLException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; User user = new User(); try { conn = ? //获取一个JDBCq接 ps = conn.prepareStatement(“…?; // 获取一个序列值来作ؓ用户标识 rs = ps.executeQuery(); rs.next(); user.userId = rs.getInt(1); //d序列gؓ新用h?BR> user.name = name; user.birthday = birthday; ps = conn.prepareStatement(“insert into ??; //插入用户数据记录的SQL ps.setInt(1,user.id); ps.setString(2,user.name); ps.setDate(3,user.birthday); ps.executeUpdate(); } finally { //q里注意一定要按照创徏的顺序关闭JDBC资源Q?BR> if(rs != null) try { rs.close(); } catch(SQLException ex) { ex.printStackTrace(); } if(ps != null) try { ps.close(); } catch(SQLException ex) { ex.printStackTrace(); } if(conn != null) try { conn.close(); } catch(SQLException ex) { ex.printStackTrace(); } } return user; } 所有的数据库访问方法都必须q行q样的包装,当我们的数据c达C定的数量后(比如十几个,几十个)Q这些方法占据了大量的代码,l护困难、出现BUGZ增多Qƈ且不易排错,其是资源漏z这U容易引h务器E_性问题的BUG?BR>Z保持数据cȝU洁Q我们可以将JDBC操作Ҏ集中C个公共工L中去完成Q这Pq个工具cM非常庞大Q但每个数据cM变得很简单,q种方式Q可以称作是DataAccessObject模式Q相当于EJB中的ValueObjectQ是q数据库细节的U对象模型?BR>q些都是最基本的存储处理,在基本增删改查(q里的查指按关键字查扑֯象)的基上,我们q需要进行复杂的匚w查询QSQLQ,q得我们的存储处理代码q一步复杂化。简单地Q我们可以写一个类似的ҎQ?BR>public static Collection findBy(String sql) throws SQLException { //?nbsp;Q这里获取JDBCq接Q?BR> Collection col = new Vector(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); while(rs.next()) { User user = new User(); user.userId = rs.getInt(1); user.name = rs.getString(2); user.birthday = rs.getDate(3); col.add(user); } return col; //?nbsp;Q同前,q里是清理JDBC资源的代码) } q就是一个查询接口的基本定义Q查询采用的语言仍是SQL?BR>如果我们需要将参数从SQL串中独立出来以节省数据库的解析时间ƈ规范化,我们q需要将查询条g作ؓ参数传递到q个Ҏ中去Q方法的接口改ؓQ?BR>public static Collection findBy(String sql, Object[] params) throws SQLException { //?BR> ps = conn.prepareStatement(sql); for(int i = 0; i < params.length; i++) ps.setObject(i+1,params[i]); //?BR>} 调用的时候sql参数中会包含一些“?”号Q如Q?BR>select ID,NAME,BIRTHDAY from USER where ?nbsp;= ? and ?nbsp;> ? and ?BR>当然Q也有一些开发团队喜Ƣ将所有可能的查询都写L一个个的专用查询方法,在其中完成对应的SQL操作Q这一点类gEJBQLQ只不过是将EJBQL中容器实现的功能通过手工~码来实现。这样做使得查询受到限制Q但可以提供更保险的接口?BR>q有一些开发h员看到每个类中写q样一些查询方法得这个类的代码变得庞大,l护ȝQ便所有的查询Ҏ攑ֈ一个公q工具cM去,只是在方法中再加入一个参敎ͼClass cls来表C需要查询哪个对象,使得每个数据cd得紧凑一些。当Ӟq样的结果是那个公共cd得异常庞大,谁维护谁倒霉Q可以说是牺牲一人,q福团队。不q如果这个h心理素质不够好、压力承受能力不强的话,一些对数据cȝ改动可能会受C的阻,q时候就是“一夫当养I万夫莫开”?BR> 现在Q我们已l实C基本对象的包装,现在才能开始考虑更多的问题。首先,我们可能会从规范化的角度出发Q给每一个属性加上读写访问器getter/setterQ在前面的UsercMQ可能我们会基本属性部分写为: public class User { private int userId; private String name; private Date birthday; //以下是针对以上属性的getter/setterQ一般可以用IDE工具生成 public int getUserId() { return userId; } public void setUserId(int value) { userId = value; } public String getName() { return name; } public void setName(String value) { name = value; } public Date getBirthday() { return birthday; } public void setBirthday(Date value) { birthday = value}; //?BR>} q样Q一个比较规范的数据cd装就完成了?BR>另外Q我们知道,面向对象概念中,一个属性可以是另一个对象,也就是说Q对象之间是存在着引用关系的,q种关系q分Z对一、一对多、多对多{几U情c从一个既定对象出发,其某个属性可以是另一个对象,也可以是包含另一l对象的集合。那么,在我们的数据包装里面Q当然最好也能方便地处理对象之间的关pR假定现在我们又有一个数据类GroupQ?BR>public class Group { public int grouId; public String groupNameQ?BR> public Set users; //set of User } q里Z单表明含义,暂不采用getter/setter?BR>而UserҎ属的Group有一个引用: public Group belongTo; 在这里,User.belongTo和Group.users是一个一对多的关pR?BR>在我们的数据cMQ如何才能实现数据库的存取呢Q就是不考虑Group.usersq个反向关系Q光是User.belongTo是一件头疼的事。如果我们在取出一个User对象时同时将其Group对象也取出来Q可以保证不会在讉K某个用户的组时得C个null。不q这h几个~点Q?BR>1. 数据cȝ存取处理QJDBCQ变得复杂,需要执行很多SQLd才行Q有时候只需要访问User的基本属性时费旉和资源;其是对集合型属性的预读取,更加可?BR>2. 如果按这个逻辑Q双向的关系处理变得危险Q很Ҏ陷入d@环,如果要避免,必须在类代码中加入一些特别的机制Q也是很ȝ的事 3. 如果对象之间的关pL更复杂的情况下,比如三个、四个对象之间互相关联,那就是一场噩梦,对代码的~写和维护都异常艰难 于是Q很多开发h员自然而然地退后一步,在UsercM只保留一个groupIdQƈ不保存Group对象Q这P可以User.belongTo属性变成intcdQ而另外写一个getterҎQ?BR>public Group getBelongTo() { return Group.findById(belongTo); } 而在GroupcMQ干脆将users属性去掉,只保留一个方法: public Set getUsers() { return new HashSet(User.findBy(“select ?nbsp;from USER where BELONG_TO=??new Object[]{ new Integer(groupId) })); } 也许l心一点的读者已l看出来了,q里的几个方法都没有SQLException捕捉Q也没有在方法中声明Q也是说是有语法错误的Q因为SQLException不是一个RuntimeException。不错,实是这P不过我们q里Z单明了v见,省掉q些处理Q以更直接清楚地表达意思?BR> q样Q实际上Q我们的对象关系包装已经名存实亡Q在cȝ内部有很多用于访问所引用对象的复杂代码,q些已经q背了我们将对象关系保持的初街有些开发h员甚至不在类中保留访问关pd象的ҎQ而是在客戯用时再去讉K另一对象cȝdҎQ如Q?BR>?BR>User user = User.getById(?; //对user对象q行一些访问,如显C其姓名{等 Group group = Group.findById(user.belongTo); //对group对象q行一些访问,如显C组名等{?BR>?BR> 在这L代码里,实际上我们已l从Ҏ上退回了关系数据库的出发点,q与直接讉K数据表有多大区别呢?只不q是在表上面套了一层貌似面向对象的皮而已。不q的是,q种方式q存在于很多应用之中?BR> 从前面提到的q些面向对象包装的细节问题,我们可以看到q种传统包装方式的一些主要的~陷Q?BR> |