在java ee se me 区别中事务在突然断电下应该如何处理,请写出具体代码

Java EE项目中的异常处理 (实在写的太好了,导致我非法转载!!!)
我的图书馆
Java EE项目中的异常处理 (实在写的太好了,导致我非法转载!!!)
为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”。笔者在初学java时也是这样认为的。如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception转化成unchecked Exception ,何时需要把unChecked Exception转化成checked Exception?异常是否应该呈现到前端页面?如何设计一个异常框架?本文将就这些问题进行探讨。
1. JAVA异常处理
在面向过程式的编程语言中,我们可以通过返回值来确定方法是否正常执行。比如在一个c语言编写的程序中,如果方法正确的执行则返回1.错误则返回0。在vb或delphi开发的应用程序中,出现错误时,我们就弹出一个消息框给用户。
通过方法的返回值我们并不能获得错误的详细信息。可能因为方法由不同的程序员编写,当同一类错误在不同的方法出现时,返回的结果和错误信息并不一致。
所以java语言采取了一个统一的异常处理机制。
什么是异常?运行时发生的可被捕获和处理的错误。
在java语言中,Exception是所有异常的父类。任何异常都扩展于Exception类。Exception就相当于一个错误类型。如果要定义一个新的错误类型就扩展一个新的Exception子类。采用异常的好处还在于可以精确的定位到导致程序出错的源代码位置,并获得详细的错误信息。
Java异常处理通过五个关键字来实现,try,catch,throw ,throws, finally。具体的异常处理结构由try….catch….finally块来实现。try块存放可能出现异常的java语句,catch用来捕获发生的异常,并对异常进行处理。Finally块用来清除程序中未释放的资源。不管理try块的代码如何返回,finally块都总是被执行。
一个典型的异常处理代码
public String getPassword(String userId)throws DataAccessException{String sql = “select password from userinfo where userid=’”+userId +”’”;String password =
Connection con =Statement s = ResultSet rs =try{ con = getConnection();//获得数据连接s = con.createStatement(); rs = s.executeQuery(sql); while(rs.next()){ password = rs.getString(1);} rs.close(); s.close(); } Catch(SqlException ex){ throw new DataAccessException(ex);} finally{ try{ if(con != null){con.close(); } } Catch(SQLException sqlEx){ throw new DataAccessException(“关闭连接失败!”,sqlEx);} }
可以看出Java的异常处理机制具有的优势:
给错误进行了统一的分类,通过扩展Exception类或其子类来实现。从而避免了相同的错误可能在不同的方法中具有不同的错误信息。在不同的方法中出现相同的错误时,只需要throw 相同的异常对象即可。
获得更为详细的错误信息。通过异常类,可以给异常更为详细,对用户更为有用的错误信息。以便于用户进行跟踪和调试程序。
把正确的返回结果与错误信息分离。降低了程序的复杂度。调用者无需要对返回结果进行更多的了解。
强制调用者进行异常处理,提高程序的质量。当一个方法声明需要抛出一个异常时,那么调用者必须使用try….catch块对异常进行处理。当然调用者也可以让异常继续往上一层抛出。
2. Checked 异常 还是 unChecked 异常?
Java异常分为两大类:checked 异常和unChecked 异常。所有继承java.lang.Exception 的异常都属于checked异常。所有继承java.lang.RuntimeException的异常都属于unChecked异常。
当一个方法去调用一个可能抛出checked异常的方法,必须通过try…catch块对异常进行捕获进行处理或者重新抛出。
我们看看Connection接口的createStatement()方法的声明。
public Statement createStatement() throws SQLE
SQLException是checked异常。当调用createStatement方法时,java强制调用者必须对SQLException进行捕获处理。
public String getPassword(String userId){try{ …… Statement s = con.createStatement(); …… Catch(SQLException sqlEx){ …… } …… }
public String getPassword(String userId)throws SQLException{Statement s = con.createStatement(); }
(当然,像Connection,Satement这些资源是需要及时关闭的,这里仅是为了说明checked 异常必须强制调用者进行捕获或继续抛出)
unChecked异常也称为运行时异常,通常RuntimeException都表示用户无法恢复的异常,如无法获得数据库连接,不能打开文件等。虽然用户也可以像处理checked异常一样捕获unChecked异常。但是如果调用者并没有去捕获unChecked异常时,编译器并不会强制你那么做。
比如一个把字符转换为整型数值的代码如下:
String str = “123”;int value = Integer.parseInt(str);
parseInt的方法签名为:
public staticint parseInt(String s)throws NumberFormatException
当传入的参数不能转换成相应的整数时,将会抛出NumberFormatException。因为NumberFormatException扩展于RuntimeException,是unChecked异常。所以调用parseInt方法时无需要try…catch
因为java不强制调用者对unChecked异常进行捕获或往上抛出。所以程序员总是喜欢抛出unChecked异常。或者当需要一个新的异常类时,总是习惯的从RuntimeException扩展。当你去调用它些方法时,如果没有相应的catch块,编译器也总是让你通过,同时你也根本无需要去了解这个方法倒底会抛出什么异常。看起来这似乎倒是一个很好的办法,但是这样做却是远离了java异常处理的真实意图。并且对调用你这个类的程序员带来误导,因为调用者根本不知道需要在什么情况下处理异常。而checked异常可以明确的告诉调用者,调用这个类需要处理什么异常。如果调用者不去处理,编译器都会提示并且是无法编译通过的。当然怎么处理是由调用者自己去决定的。
所以Java推荐人们在应用代码中应该使用checked异常。就像我们在上节提到运用异常的好外在于可以强制调用者必须对将会产生的异常进行处理。包括在《java Tutorial》等java官方文档中都把checked异常作为标准用法。
使用checked异常,应意味着有许多的try…catch在你的代码中。当在编写和处理越来越多的try…catch块之后,许多人终于开始怀疑checked异常倒底是否应该作为标准用法了。
甚至连大名鼎鼎的《thinking in java》的作者Bruce Eckel也改变了他曾经的想法。Bruce Eckel甚至主张把unChecked异常作为标准用法。并发表文章,以试验checked异常是否应该从java中去掉。Bruce Eckel语:“当少量代码时,checked异常无疑是十分优雅的构思,并有助于避免了许多潜在的错误。但是经验表明,对大量代码来说结果正好相反”
关于checked异常和unChecked异常的详细讨论可以参考
Alan Griffiths
Bruce Eckel
《java Tutorial》
使用checked异常会带来许多的问题。
checked异常导致了太多的try…catch 代码
可能有很多checked异常对开发人员来说是无法合理地进行处理的,比如SQLException。而开发人员却不得不去进行try…catch。当开发人员对一个checked异常无法正确的处理时,通常是简单的把异常打印出来或者是干脆什么也不干。特别是对于新手来说,过多的checked异常让他感到无所适从。
…… Statement s = con.createStatement(); …… Catch(SQLException sqlEx){ sqlEx.PrintStackTrace(); } 或者 try{ …… Statement s = con.createStatement(); …… Catch(SQLException sqlEx){ //什么也不干 }
checked异常导致了许多难以理解的代码产生
当开发人员必须去捕获一个自己无法正确处理的checked异常,通常的是重新封装成一个新的异常后再抛出。这样做并没有为程序带来任何好处。反而使代码晚难以理解。
就像我们使用JDBC代码那样,需要处理非常多的try…catch.,真正有用的代码被包含在try…catch之内。使得理解这个方法变理困难起来
checked异常导致异常被不断的封装成另一个类异常后再抛出
void methodA()throws ExceptionA{….. throw new ExceptionA();} public void methodB()throws ExceptionB{try{ methodA(); …… }catch(ExceptionA ex){
throw new ExceptionB(ex);} } Public void methodC()throws ExceptinC{try{ methodB(); … } catch(ExceptionB ex){
throw new ExceptionC(ex);} }
我们看到异常就这样一层层无休止的被封装和重新抛出。
checked异常导致破坏接口方法
一个接口上的一个方法已被多个类使用,当为这个方法额外添加一个checked异常时,那么所有调用此方法的代码都需要修改。
可见上面这些问题都是因为调用者无法正确的处理checked异常时而被迫去捕获和处理,被迫封装后再重新抛出。这样十分不方便,并不能带来任何好处。在这种情况下通常使用unChecked异常。
chekced异常并不是无一是处,checked异常比传统编程的错误返回值要好用得多。通过编译器来确保正确的处理异常比通过返回值判断要好得多。
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常
倒底什么时候使用checked异常,什么时候使用unChecked异常?并没有一个绝对的标准。但是笔者可以给出一些建议
当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。
这个异常仅是少数比较高级的调用者才能处理,一般的调用者不能正确的处理。使用unchecked异常。有能力处理的调用者可以进行高级处理,一般调用者干脆就不处理。
这个异常是一个非常严重的错误,如数据库连接错误,文件无法打开等。或者这些异常是与外部环境相关的。不是重试可以解决的。使用unchecked异常。因为这种异常一旦出现,调用者根本无法处理。
如果不能确定时,使用unchecked异常。并详细描述可能会抛出的异常,以让调用者决定是否进行处理。
3. 设计一个新的异常类
在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。
如java 代码IllegalArgumentException, UnsupportedOperationException
不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。
public void methodA()throws ExceptionA{….. throw new ExceptionA();}
方法methodA声明会抛出ExceptionA.
public void methodB()throws ExceptionB
methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,ExceptionA是无法处理的,所以ExceptionA应该继续往上抛出。一个办法是把methodB声明会抛出ExceptionA.但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。
另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。
public void methodB()throws ExceptionB{try{ methodA(); …… }catch(ExceptionA ex){throw new ExceptionB(ex);} }
如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。
所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。
public Class ExceptionB
extends Exception{ private T public ExceptionB(String msg, Throwable ex){super(msg);
this.cause = } public ExceptionB(String msg){super(msg); } public ExceptionB(Throwable ex){this.cause = } }
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。
public void printStackTrace(PrintStrean ps){if(cause == null){super.printStackTrace(ps);}else{ ps.println(this);cause.printStackTrace(ps); } }
一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException
public NestedException
extends Exception{ private T public NestedException (String msg){super(msg); } public NestedException(String msg, Throwable ex){super(msg); This.cause = } public Throwable getCause(){ return (this.cause ==null ?this :this.cause);} public getMessage(){ String message = super.getMessage();Throwable cause = getCause(); if(cause !=
null){ message = message + “;nested Exception is ” + }
} public void printStackTrace(PrintStream ps){if(getCause ==
null){ super.printStackTrace(ps);
}else{ ps.println(this);getCause().printStackTrace(ps); } } public void printStackTrace(PrintWrite pw){if(getCause() ==
null){ super.printStackTrace(pw);
} else{ pw.println(this);getCause().printStackTrace(pw); } } public void printStackTrace(){printStackTrace(System.error); } }
同样要设计一个unChecked异常类也与上面一样。只是需要继承RuntimeException。
4. 如何记录异常
作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。
public String getPassword(String userId)throws NoSuchUserException{UserInfo user = userDao.queryUserById(userId); If(user == null){<(“找不到该用户信息,userId=”+userId); throw new NoSuchUserException(“找不到该用户信息,userId=”+userId);} else{ return user.getPassword();
} } public void sendUserPassword(String userId)throws Exception {UserInfo user =try{ user = getPassword(userId); //…….. sendMail(); // }catch(NoSuchUserException ex)(logger.error(“找不到该用户信息:”+userId+ex); throw new Exception(ex);}
我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。
笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?
异常应该在最初产生的位置记录!
如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。
如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常
public Date getDate(String str){Date applyDate =
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);try{ applyDate = format.parse(applyDateStr); } catch(ParseException ex){
//乎略,当格式错误时,返回null } return applyD }
捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息
try{ …… String sql=”select * from userinfo”; Statement s = con.createStatement(); …… Catch(SQLException sqlEx){ Logger.error(“sql执行错误”+sql+sqlEx); }
究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。
class BusinessException extends Exception {private void logTrace() {StringBuffer buffer=new StringBuffer();buffer.append("Business Error in Class: ");buffer.append(getClassName()); buffer.append(",method: ");
buffer.append(getMethodName()); buffer.append(",messsage: ");
buffer.append(this.getMessage());logger.error(buffer.toString()); } public BusinessException(String s) {super(s); race(); }
这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了“类的职责分配原则”,是一种不好的设计。记录异常不属于异常类的行为,记录异常应该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常同应该给更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决问题。
虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。
5. J2EE项目中的异常处理
目前,J2ee项目一般都会从逻辑上分为多层。比较经典的分为三层:表示层,业务层,集成层(包括数据库访问和外部系统的访问)。
J2ee项目有着其复杂性,J2ee项目的异常处理需要特别注意几个问题。
在分布式应用时,我们会遇到许多checked异常。所有RMI调用(包括EJB远程接口调用)都会抛出java.rmi.RemoteE同时RemoteException是checked异常,当我们在业务系统中进行远程调用时,我们都需要编写大量的代码来处理这些checked异常。而一旦发生RemoteException这些checked异常对系统是非常严重的,几乎没有任何进行重试的可能。也就是说,当出现RemoteException这些可怕的checked异常,我们没有任何重试的必要性,却必须要编写大量的try…catch代码去处理它。一般我们都是在最底层进行RMI调用,只要有一个RMI调用,所有上层的接口都会要求抛出RemoteException异常。因为我们处理RemoteException的方式就是把它继续往上抛。这样一来就破坏了我们业务接口。RemoteException这些J2EE系统级的异常严重的影响了我们的业务接口。我们对系统进行分层的目的就是减少系统之间的依赖,每一层的技术改变不至于影响到其它层。
// public class UserSoaImplimplements UserSoa{public UserInfo getUserInfo(String userId)throws RemoteException{//…… 远程方法调用. //…… } } public interface UserManager{public UserInfo getUserInfo(Stirng userId)throws RemoteE}
同样JDBC访问都会抛出SQLException的checked异常。
为了避免系统级的checked异常对业务系统的深度侵入,我们可以为业务方法定义一个业务系统自己的异常。针对像SQLException,RemoteException这些非常严重的异常,我们可以新定义一个unChecked的异常,然后把SQLException,RemoteException封装成unChecked异常后抛出。
如果这个系统级的异常是要交由上一级调用者处理的,可以新定义一个checked的业务异常,然后把系统级的异常封存装成业务级的异常后再抛出。
一般地,我们需要定义一个unChecked异常,让集成层接口的所有方法都声明抛出这unChecked异常。
public DataAccessExceptionextends RuntimeException{…… } public interface UserDao{public String getPassword(String userId)throws DataAccessE} public class UserDaoImplimplements UserDAO{public String getPassword(String userId)throws DataAccessException{String sql = “select password from userInfo where userId= ‘”+userId+”’”;try{ … //JDBC调用 s.executeQuery(sql); … }catch(SQLException ex){
throw new DataAccessException(“数据库查询失败”+sql,ex);} } }
定义一个checked的业务异常,让业务层的接口的所有方法都声明抛出Checked异常.
public class BusinessExceptionextends Exception{….. } public interface UserManager{public Userinfo copyUserInfo(Userinfo user)throws BusinessException{Userinfo newUser =try{ newUser = (Userinfo)user.clone(); }catch(CloneNotSupportedException ex){throw new BusinessException(“不支持clone方法:”+Userinfo.class.getName(),ex);} } }
J2ee表示层应该是一个很薄的层,主要的功能为:获得页面请求,把页面的参数组装成POJO对象,调用相应的业务方法,然后进行页面转发,把相应的业务数据呈现给页面。表示层需要注意一个问题,表示层需要对数据的合法性进行校验,比如某些录入域不能为空,字符长度校验等。
J2ee从页面所有传给后台的参数都是字符型的,如果要求输入数值或日期类型的参数时,必须把字符值转换为相应的数值或日期值。
如果表示层代码校验参数不合法时,应该返回到原始页面,让用户重新录入数据,并提示相关的错误信息。
通常把一个从页面传来的参数转换为数值,我们可以看到这样的代码
ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{String ageStr = request.getParameter(“age”); int age = Integer.parse(ageStr);………… String birthDayStr = request.getParameter(“birthDay”); SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);Date birthDay = format.parse(birthDayStr); }
上面的代码应该经常见到,但是当用户从页面录入一个不能转换为整型的字符或一个错误的日期值。
Integer.parse()方法被抛出一个NumberFormatException的unChecked异常。但是这个异常绝对不是一个致命的异常,一般当用户在页面的录入域录入的值不合法时,我们应该提示用户进行重新录入。但是一旦抛出unchecked异常,就没有重试的机会了。像这样的代码造成大量的异常信息显示到页面。使我们的系统看起来非常的脆弱。
同样,SimpleDateFormat.parse()方法也会抛出ParseException的unChecked异常。
这种情况我们都应该捕获这些unChecked异常,并给提示用户重新录入。
ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{String ageStr = request.getParameter(“age”); String birthDayStr = request.getParameter(“birthDay”);
int age = 0;Date birthDay =try{ age=Integer.parse(ageStr); }catch(NumberFormatException ex){error.reject(“age”,”不是合法的整数值”); } ………… try{ SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);birthDay = format.parse(birthDayStr); }catch(ParseException ex){
error.reject(“birthDay”,”不是合法的日期,请录入’MM/dd/yyy’格式的日期”);
在表示层一定要弄清楚调用方法的是否会抛出unChecked异常,什么情况下会抛出这些异常,并作出正确的处理。
在表示层调用系统的业务方法,一般情况下是无需要捕获异常的。如果调用的业务方法抛出的异常相当于第二个返回值时,在这种情况下是需要捕获
TA的最新馆藏[转]&一、使用注解可以解决JavaBean和数据库中表名不一致、字段名不一致、字段数量不一致的问题。
  1.Sun公司给jdbc提供的注解
  @Table、@Column、@Id、@OneToMany、@OneToOne、@ManyToMany
  2.小练习:对JavaBean的某些字段进行注解、对JavaBean名称进行注解以匹配数据库表名。
   Person.java
package com.kdyzm.
import javax.persistence.C
import javax.persistence.T
* 使用注解可以解决数据库表名和类名不一致的问题,可以解决字段名不匹配的问题,可以解决字段数量不匹配的问题
@Table(name="user")//实际的表名为user,而不是person
public class Person {
@Column(name="id")
@Column(name="name")
@Column(name="age")
private int
private String personA//该字段在数据库表中斌不存在。
public Person() {
public Person(String personid, String personname, int personage,
String personAddress) {
this.personid =
this.personname =
this.personage =
this.personAddress = personA
public String getPersonid() {
public void setPersonid(String personid) {
this.personid =
public String getPersonname() {
public void setPersonname(String personname) {
this.personname =
public int getPersonage() {
public void setPersonage(int personage) {
this.personage =
public String getPersonAddress() {
return personA
public void setPersonAddress(String personAddress) {
this.personAddress = personA
public String toString() {
return "Person [personid=" + personid + ", personname=" + personname
+ ", personage=" + personage + "]";
Person.java
   UserDao.java
1 package com.kdyzm.
3 import com.kdyzm.dbutils.DataSourceUtils_c3p0;
4 import com.kdyzm.dbutils.QueryR
5 import com.kdyzm.domain.P
7 //注解在数据库中的使用方法
8 public class UserDao {
public static void main(String[] args) throws Exception {
Person p=new Person();
p.setPersonid("");
p.setPersonname("小强");
p.setPersonage(24);
p.setPersonAddress("山东理工大学");
QueryRunner run=new QueryRunner(DataSourceUtils_c3p0.getDataSource());
run.save(p);
  重写QueryRunner类(dbutils.jar包)
package com.kdyzm.
* 重写QueryRunner类,将异常全部捕捉
* 新增加一个方法save,这个方法利用反射机制可以省去书写sql的麻烦。
import java.beans.PropertyD
import java.lang.reflect.F
import java.sql.C
import java.sql.PreparedS
import java.sql.ResultS
import java.sql.SQLE
import java.sql.S
import javax.persistence.C
import javax.persistence.T
import javax.sql.DataS
import mons.dbutils.ResultSetH
public class QueryRunner extends mons.dbutils.QueryRunner{
public &T&T save(T t)throws Exception
Class&?& cls=t.getClass();
* 获取表名,这里理应当先进行判断是否存在注解即调用isAnnotationPresents方法,
* 但是因为一定存在注解,所以不进行判断了
Table table=cls.getAnnotation(Table.class);
//首先获取表名
String tableName=table.name();
//组成sql语句,这是重中自重
String sql="insert into "+tableN
String columns="(";//拼接字段名称
String values="values(";//拼接值
//获取所有声明的字段名称
Field[]fields=cls.getDeclaredFields();
for(Field field:fields)
if(field.isAnnotationPresent(Column.class))//如果进行了注解的话,这句话是解决字段数量不匹配的核心
field.setAccessible(true);//设置可以暴力访问
String columnName=field.getName();//本来的字段名
Column column=field.getAnnotation(Column.class);
//优先考虑有没有注解的字段名,如果没有注解的话则使用原来的字段名否则使用注解声明的字段名
if(column.name()!=null||!column.name().equals(""))//如果有注解的话使用注解上声明的字段名
columnName=column.name();
//获取列值
Object value=field.get(t);
if(columns.equals("("))
columns+=columnN
if(value instanceof String)
values+="'"+value+"'";
columns+=","+columnN
if(value instanceof String)
values+=",'"+value+"'";
values+=","+
columns+=") ";
values+=")";
System.out.println(sql);
update(sql);
public QueryRunner() {
public QueryRunner(boolean pmdKnownBroken) {
super(pmdKnownBroken);
public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
super(ds, pmdKnownBroken);
public QueryRunner(DataSource ds) {
super(ds);
public int[] batch(Connection conn, String sql, Object[][] params)
throws SQLException {
return super.batch(conn, sql, params);
public int[] batch(String sql, Object[][] params) throws SQLException{
return super.batch(sql, params);
public &T& T insert(Connection conn, String sql, ResultSetHandler&T& rsh,
Object... params) throws SQLException {
return super.insert(conn, sql, rsh, params);
public &T& T insert(Connection conn, String sql, ResultSetHandler&T& rsh)
throws SQLException {
return super.insert(conn, sql, rsh);
public &T& T insert(String sql, ResultSetHandler&T& rsh, Object... params)
throws SQLException {
return super.insert(sql, rsh, params);
public &T& T insert(String sql, ResultSetHandler&T& rsh)
throws SQLException {
return super.insert(sql, rsh);
public &T& T insertBatch(Connection conn, String sql,
ResultSetHandler&T& rsh, Object[][] params) throws SQLException {
return super.insertBatch(conn, sql, rsh, params);
public &T& T insertBatch(String sql, ResultSetHandler&T& rsh,
Object[][] params) throws SQLException {
return super.insertBatch(sql, rsh, params);
public &T& T query(Connection conn, String sql, Object param,
ResultSetHandler&T& rsh) throws SQLException {
return super.query(conn, sql, param, rsh);
public &T& T query(Connection conn, String sql, Object[] params,
ResultSetHandler&T& rsh) throws SQLException {
return super.query(conn, sql, params, rsh);
public &T& T query(Connection conn, String sql, ResultSetHandler&T& rsh,
Object... params) throws SQLException {
return super.query(conn, sql, rsh, params);
public &T& T query(Connection conn, String sql, ResultSetHandler&T& rsh)
throws SQLException {
return super.query(conn, sql, rsh);
public &T& T query(String sql, Object param, ResultSetHandler&T& rsh)
throws SQLException {
return super.query(sql, param, rsh);
public &T& T query(String sql, Object[] params, ResultSetHandler&T& rsh)
throws SQLException {
return super.query(sql, params, rsh);
public &T& T query(String sql, ResultSetHandler&T& rsh, Object... params)
throws SQLException {
return super.query(sql, rsh, params);
public &T& T query(String sql, ResultSetHandler&T& rsh) throws SQLException {
return super.query(sql, rsh);
public int update(Connection conn, String sql, Object... params)
throws SQLException {
return super.update(conn, sql, params);
public int update(Connection conn, String sql, Object param)
throws SQLException {
return super.update(conn, sql, param);
public int update(Connection conn, String sql) throws SQLException {
return super.update(conn, sql);
public int update(String sql, Object... params) throws SQLException {
return super.update(sql, params);
public int update(String sql, Object param) throws SQLException {
return super.update(sql, param);
public int update(String sql) throws SQLException {
return super.update(sql);
protected void close(Connection conn) throws SQLException {
super.close(conn);
protected void close(ResultSet rs) throws SQLException {
super.close(rs);
protected void close(Statement stmt) throws SQLException {
super.close(stmt);
public void fillStatement(PreparedStatement arg0, Object... arg1)
throws SQLException {
super.fillStatement(arg0, arg1);
public void fillStatementWithBean(PreparedStatement arg0, Object arg1,
PropertyDescriptor[] arg2) throws SQLException {
super.fillStatementWithBean(arg0, arg1, arg2);
public void fillStatementWithBean(PreparedStatement arg0, Object arg1,
String... arg2) throws SQLException {
super.fillStatementWithBean(arg0, arg1, arg2);
public DataSource getDataSource() {
return super.getDataSource();
public boolean isPmdKnownBroken() {
return super.isPmdKnownBroken();
protected Connection prepareConnection() throws SQLException {
return super.prepareConnection();
protected PreparedStatement prepareStatement(Connection conn, String sql,
int returnedKeys) throws SQLException {
return super.prepareStatement(conn, sql, returnedKeys);
protected PreparedStatement prepareStatement(Connection conn, String sql)
throws SQLException {
return super.prepareStatement(conn, sql);
protected void rethrow(SQLException cause, String sql, Object... params)
throws SQLException {
super.rethrow(cause, sql, params);
protected ResultSet wrap(ResultSet rs) {
return super.wrap(rs);
QueryRunner.java
  重写之后的类中多出了一个方法:save方法,该方法接受一个JavaBean参数,并使用反射对该JavaBean进行解析,得到该JavaBean中符合要求的字段,并得到每个字段对应的值,关键是凭借sql语句的过程。
  C3p0数据库连接池工具类:
package com.kdyzm.
* 数据库连接池工具类。
import java.sql.C
import java.sql.SQLE
import javax.sql.DataS
import com.mchange.boPooledDataS
public class DataSourceUtils_c3p0 {
private static ThreadLocal&Connection&tl=new ThreadLocal&Connection&();
private static DataSource ds=null;
ds=new ComboPooledDataSource("namedconfig");
public static Connection getConnection(){
Connection conn=tl.get();
if(conn==null)
conn=ds.getConnection();
tl.set(conn);//这句代码十分重要,千万不能忘(将当前线程和请求的Connection对象绑定)
} catch (SQLException e) {
e.printStackTrace();
public static DataSource getDataSource(){
public static void remove(){
tl.remove();//这句代码也十分重要,千万不能忘掉,将会在TransactionFilter中调用
DataSourceUtils_c3p0.java
  c3p0数据库连接池配置文件:
&?xml version="1.0" encoding="UTF-8"?&
&c3p0-config&
&!-- 默认配置,只可以出现一次 --&
&default-config&
&!-- 连接超时设置30秒 --&
&property name="checkoutTimeout"&30000&/property&
&!-- 30秒检查一次connection的空闲 --&
&property name="idleConnectionTestPeriod"&30&/property&
&!--初始化的池大小 --&
&property name="initialPoolSize"&2&/property&
&!-- 最多的一个connection空闲时间 --&
&property name="maxIdleTime"&30&/property&
&!-- 最多可以有多少个连接connection --&
&property name="maxPoolSize"&10&/property&
&!-- 最少的池中有几个连接 --&
&property name="minPoolSize"&2&/property&
&!-- 批处理的语句--&
&property name="maxStatements"&50&/property&
&!-- 每次增长几个连接 --&
&property name="acquireIncrement"&3&/property&
&property name="driverClass"&com.mysql.jdbc.Driver&/property&
&property name="jdbcUrl"&
&![CDATA[jdbc:mysql://10.6.112.200:3306/test?useUnicode=true&characterEncoding=UTF-8]]&
&/property&
&property name="user"&root&/property&
&property name="password"&5a6f38&/property&
&/default-config&
&named-config name="namedconfig"&
&!-- 连接超时设置30秒 --&
&property name="checkoutTimeout"&30000&/property&
&!-- 30秒检查一次connection的空闲 --&
&property name="idleConnectionTestPeriod"&30&/property&
&!--初始化的池大小 --&
&property name="initialPoolSize"&2&/property&
&!-- 最多的一个connection空闲时间 --&
&property name="maxIdleTime"&30&/property&
&!-- 最多可以有多少个连接connection --&
&property name="maxPoolSize"&4&/property&
&!-- 最少的池中有几个连接 --&
&property name="minPoolSize"&2&/property&
&!-- 批处理的语句--&
&property name="maxStatements"&50&/property&
&!-- 每次增长几个连接 --&
&property name="acquireIncrement"&2&/property&
&property name="driverClass"&com.mysql.jdbc.Driver&/property&
&property name="jdbcUrl"&
&![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]&
&/property&
&property name="user"&root&/property&
&property name="password"&5a6f38&/property&
&/named-config&
&/c3p0-config&
c3p0-config.xml
  3.运行结果:
20:10:05 com.mchange.v2.log.slf4j.Slf4jMLog$Slf4jMLogger$InfoLogger log
信息: MLog clients using slf4j logging.
20:10:07 com.mchange.v2.log.slf4j.Slf4jMLog$Slf4jMLogger$InfoLogger log
信息: Initializing c3p0-0.9.5 [built 02-January-:04 -0500; debug? trace: 10]
insert into user(id,name,age) values('','小强',24)
20:10:08 com.mchange.v2.log.slf4j.Slf4jMLog$Slf4jMLogger$InfoLogger log
信息: Initializing c3p0 pool... com.mchange.boPooledDataSource [ acquireIncrement -& 2, acquireRetryAttempts -& 30, acquireRetryDelay -& 1000, autoCommitOnClose -& false, automaticTestTable -& null, breakAfterAcquireFailure -& false, checkoutTimeout -& 30000, connectionCustomizerClassName -& null, connectionTesterClassName -& com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -& caller, dataSourceName -& namedconfig, debugUnreturnedConnectionStackTraces -& false, description -& null, driverClass -& com.mysql.jdbc.Driver, extensions -& {}, factoryClassLocation -& null, forceIgnoreUnresolvedTransactions -& false, forceUseNamedDriverClass -& false, identityToken -& 2s4ze59akeetzj1p6tfdu|a32b, idleConnectionTestPeriod -& 30, initialPoolSize -& 2, jdbcUrl -& jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8, maxAdministrativeTaskTime -& 0, maxConnectionAge -& 0, maxIdleTime -& 30, maxIdleTimeExcessConnections -& 0, maxPoolSize -& 4, maxStatements -& 50, maxStatementsPerConnection -& 0, minPoolSize -& 2, numHelperThreads -& 3, preferredTestQuery -& null, privilegeSpawnedThreads -& false, properties -& {user=******, password=******}, propertyCycle -& 0, statementCacheNumDeferredCloseThreads -& 0, testConnectionOnCheckin -& false, testConnectionOnCheckout -& false, unreturnedConnectionTimeout -& 0, userOverrides -& {}, usesTraditionalReflectiveProxies -& false ]
数据库中已经多出了一条记录。表名已经保存到数据库中成功。
  4.源代码:
二、反射+注解+动态代理在事务中的应用service层
  1.代理
    代理就使用一种方法在一个对象调用一个方法的时候拦截该方法的执行并改为执行另外一个动作。
  2.代理的核心类
    (1)Proxy:在内存中生成该接口的一个实例。
    (2)InvocationHandler:执行句柄,在执行代理类的方法的时候,该句柄会拦截所有代理类的方法。
  3.使用代理类的要求:被代理类必须至少实现一种接口。
  4.对List进行代理。
package com.kdyzm.
import java.lang.reflect.InvocationH
import java.lang.reflect.M
import java.lang.reflect.P
import java.util.ArrayL
import java.util.L
* 演示对List进行代理,只能对List进行代理。
* @author kdyzm
public class ProxyForListDemo {
public static void main(String[] args) {
final List&String&list=new ArrayList&String&();
Object proxy=Proxy.newProxyInstance(ProxyForListDemo.class.getClassLoader(),
new Class[]{List.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(method.getName()+"方法被调用!");
return method.invoke(list, args);
@SuppressWarnings("unchecked")
List&String&l=(List&String&)
l.add("小强!");
System.out.println(l);
  运行结果:
add方法被调用!
toString方法被调用!
三、对所实现接口的类进行代理
  1.使用一个类封装代理的过程。该类实现了InvocationHandler接口。
package com.kdyzm.
import java.lang.reflect.InvocationH
import java.lang.reflect.M
import java.lang.reflect.P
* 演示对所有拥有接口的类进行代理,并且该类实现了InvocationHandler接口。
* 这种方式是推荐的代理方式。
public class ProxyForAllClassHasInterface implements InvocationHandler{
private ProxyForAllClassHasInterface(Object src)
public static Object factory(Object src)
Object aim=Proxy.newProxyInstance(ProxyForAllClassHasInterface.class.getClassLoader(),
src.getClass().getInterfaces(),
new ProxyForAllClassHasInterface(src));
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(method.getName()+"方法被调用ProxyForAllClassHasInterface!");
return method.invoke(src, args);//调用src的方法。
  这种方式是推荐的使用方式。
   2.测试该工具类。
package com.kdyzm.
import java.util.ArrayL
import java.util.HashM
import java.util.L
import java.util.M
* 对ProxyForAllClassHasInterface类进行测试!
public class Test {
public static void main(String[] args) {
List&String&list=new ArrayList&String&();
list=(List&String&) ProxyForAllClassHasInterface.factory(list);
list.add("你好,小强!");
System.out.println(list);
Map&String,String&map=new HashMap&String,String&();
map=(Map&String, String&) ProxyForAllClassHasInterface.factory(map);
map.put("", "小强");
System.out.println(map);
  3.运行结果
add方法被调用ProxyForAllClassHasInterface!
toString方法被调用ProxyForAllClassHasInterface!
[你好,小强!]
put方法被调用ProxyForAllClassHasInterface!
toString方法被调用ProxyForAllClassHasInterface!
四、反射+注解+动态代理在事务中的应用service层
  1.解决的问题:事务使用OSIV模式并不高效,而且结构比较复杂,为了解决这个问题,可以使用反射+注解+动态代理的方式,这将称为最终的解决方式。
  使用该方式的灵活性极高,事务的处理过程在service层解决,但是在serviece层的代码中又看不出来,实际上的事务处理在代理类中实现,service是否开启事务,仅仅只需要一句代码就可以解决。
  2.核心类:代理service的ProxyForTransactionService类。
package com.kdyzm.
import java.lang.reflect.InvocationH
import java.lang.reflect.M
import java.lang.reflect.P
import java.sql.C
import com.kdyzm.demo.ProxyForAllClassHasI
import com.kdyzm.myannotation.T
* 对Service类进行代理,拦截特定的方法并进行修改,实现InvocationHandler接口是经典的做法。
* 该类可以放在工具类中。
public class ProxyForTransactionService implements InvocationHandler{
private ProxyForTransactionService(Object src)
public static Object factory(Object src)
Object aim=Proxy.newProxyInstance(ProxyForAllClassHasInterface.class.getClassLoader(),
src.getClass().getInterfaces(),
new ProxyForTransactionService(src));
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Connection connection=null;
Object returnValue=null;
if(!method.isAnnotationPresent(Transactionflag.class))//首先判断是否是经过注解的方法,如果是没有经过注解的方法则表明不需要开启事务!
System.out.println("不需要开启事务的方法:"+method.getName());
return method.invoke(src, args);
System.out.println("需要开启事务的方法:"+method.getName());
//获取连接
connection=DataSourceUtils_c3p0.getConnection();
//设置事务的开始
connection.setAutoCommit(false);
System.out.println("已经开启事务!");
//调用方法
method.invoke(src, args);
System.out.println("提交!结束事务!");
catch(Exception e)
connection.rollback();
System.out.println("回滚!结束事务!");
throw//为什么能抛,因为Throwable是Exception的父类。
connection.close();
DataSourceUtils_c3p0.remove();
return returnV
  2.要对service层的类进行代理,这些类必须至少实现一个接口,所以需要定义一个接口。
package com.kdyzm.
import com.kdyzm.myannotation.T
public interface TransactionInterface {
@Transactionflag//表示该方法需要使用事务
public void save();//定义save事务解决方法。
//不加注解表示该方法不需要使用事务。
public void query();
  3.实现该接口的类看不出有事务的处理,但实际上已经对某些方法开启了事务。
package com.kdyzm.
import com.kdyzm.dao.Dao1;
import com.kdyzm.dao.Dao2;
import com.kdyzm.interfaces.TransactionI
public class SaveService implements TransactionInterface{
Dao1 dao1=new Dao1();
Dao2 dao2=new Dao2();
public void save()
dao1.save();
dao2.save();
public void query() {
dao1.query();
  4.自定义一个注解对接口上的某些方法进行注解。
package com.kdyzm.
import java.lang.annotation.ElementT
import java.lang.annotation.R
import java.lang.annotation.RetentionP
import java.lang.annotation.T
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface Transactionflag {
  5.测试
  (1)当没有异常发生的时候
  控制台输出结果:
需要开启事务的方法:save
七月 01, :20 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
七月 01, :21 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5 [built 02-January-:04 -0500; debug? trace: 10]
七月 01, :21 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.boPooledDataSource [ acquireIncrement -& 2, acquireRetryAttempts -& 30, acquireRetryDelay -& 1000, autoCommitOnClose -& false, automaticTestTable -& null, breakAfterAcquireFailure -& false, checkoutTimeout -& 30000, connectionCustomizerClassName -& null, connectionTesterClassName -& com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -& caller, dataSourceName -& namedconfig, debugUnreturnedConnectionStackTraces -& false, description -& null, driverClass -& com.mysql.jdbc.Driver, extensions -& {}, factoryClassLocation -& null, forceIgnoreUnresolvedTransactions -& false, forceUseNamedDriverClass -& false, identityToken -& 2s4ze59akgdv042f54zv|142f828, idleConnectionTestPeriod -& 30, initialPoolSize -& 2, jdbcUrl -& jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8, maxAdministrativeTaskTime -& 0, maxConnectionAge -& 0, maxIdleTime -& 30, maxIdleTimeExcessConnections -& 0, maxPoolSize -& 4, maxStatements -& 50, maxStatementsPerConnection -& 0, minPoolSize -& 2, numHelperThreads -& 3, preferredTestQuery -& null, privilegeSpawnedThreads -& false, properties -& {user=******, password=******}, propertyCycle -& 0, statementCacheNumDeferredCloseThreads -& 0, testConnectionOnCheckin -& false, testConnectionOnCheckout -& false, unreturnedConnectionTimeout -& 0, userOverrides -& {}, usesTraditionalReflectiveProxies -& false ]
已经开启事务!
提交!结束事务!
  检查数据库,多出了两条记录。
  (2)当有异常发生时。
需要开启事务的方法:save
七月 01, :05 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
七月 01, :05 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5 [built 02-January-:04 -0500; debug? trace: 10]
七月 01, :05 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.boPooledDataSource [ acquireIncrement -& 2, acquireRetryAttempts -& 30, acquireRetryDelay -& 1000, autoCommitOnClose -& false, automaticTestTable -& null, breakAfterAcquireFailure -& false, checkoutTimeout -& 30000, connectionCustomizerClassName -& null, connectionTesterClassName -& com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -& caller, dataSourceName -& namedconfig, debugUnreturnedConnectionStackTraces -& false, description -& null, driverClass -& com.mysql.jdbc.Driver, extensions -& {}, factoryClassLocation -& null, forceIgnoreUnresolvedTransactions -& false, forceUseNamedDriverClass -& false, identityToken -& 2s4ze59akgg3q3p33eio|142f828, idleConnectionTestPeriod -& 30, initialPoolSize -& 2, jdbcUrl -& jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8, maxAdministrativeTaskTime -& 0, maxConnectionAge -& 0, maxIdleTime -& 30, maxIdleTimeExcessConnections -& 0, maxPoolSize -& 4, maxStatements -& 50, maxStatementsPerConnection -& 0, minPoolSize -& 2, numHelperThreads -& 3, preferredTestQuery -& null, privilegeSpawnedThreads -& false, properties -& {user=******, password=******}, propertyCycle -& 0, statementCacheNumDeferredCloseThreads -& 0, testConnectionOnCheckin -& false, testConnectionOnCheckout -& false, unreturnedConnectionTimeout -& 0, userOverrides -& {}, usesTraditionalReflectiveProxies -& false ]
已经开启事务!
回滚!结束事务!
七月 01, :06 下午 org.apache.catalina.core.StandardWrapperValve invoke各种异常信息略。
  检查数据库,一条记录也没有。
  5.怎样判别一个方法是否需要开启事务?比如查询操作就不需要开启事务。
  解决方法:对需要进行事务处理的方法进行注解,在代理工具类中进行判断。
  6.源代码:
  7.注意事项:代理之后的对象强制转换的结果一定是被代理类的接口的实例,而不是被代理类的实例。这点是十分重要的。如果强转成被代理类的实例,则一定会强制转换异常的错误。
阅读(...) 评论()}

我要回帖

更多关于 java ee下载 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信