我来也! 请教一下,在纯的JDBC中,如何项目采用分层了DAO层,服务层和SERVLET控制层,,那么怎么在servlet中启用JDBC的事务?
解决方案 »
- Exception in thread "main" java.lang.NullPointerException
- 从一个txt读取字符串最好用什么方法?
- JAVA,猜数小程序求教~~
- 一个简单的QQ聊天程序,基于服务器很客户端的,但是我写好以后调试了两天都没调试出来。。。不知道错在哪里(希望大家帮帮忙,我没多少分数能给了,只剩20分)
- 看THINKIN IN JAVA 第3版 不懂的地方
- JDK1.4中的打印问题!
- 关于hashmap,很急,帮帮忙
- 就<<thinking in java 2th>>中的练习题,提几个概念问题
- jdbc 连接oracle 数据库问题.
- Image只能画在Applet里吗?请进来看看这个程序
- 高分请教大地坐标与经纬度的投影方法
- Intel Xeon 64位windows server 2003 能运行哪个jdk?紧急求助!!!
楼主【ghyghost】截止到2008-07-05 18:18:10的历史汇总数据(不包括此帖):
发帖的总数量:107 发帖的总分数:1840
结贴的总数量:99 结贴的总分数:1750
无满意结贴数:27 无满意结贴分:590
未结的帖子数:8 未结的总分数:90
结贴的百分比:92.52 % 结分的百分比:95.11 %
无满意结贴率:27.27 % 无满意结分率:33.71 %
值得尊敬
如果服务层没有用spring,不能用AOP拦截的话,可以在filter中控制事务,但粒度有点太大了有一篇文章 “用 JOTM 向Servlet中添加事务”,不知道能否对你有所帮助
http://tech.ccidnet.com/art/1077/20030820/60272_1.html
try {
conn = ...;
conn.setAutoCommit(false);
callSomeFunction(conn, other params);
conn.commit();
}
catch(Exception e) {
conn.rollback();
}
finally{
conn.setAutoCommit(true);
//下面把conn关闭回收了
....
}不知道回答了你的问题没
小兵,总感觉不太爽的感觉就是在SERVLET中将CONN对象由参数传给服务层有些不太爽,这样层一层之间就耦合了,服务层应该调用DAO方法才对,呵呵
如果只是传Connection的话,也算不上耦合。
我认为不应该是服务层控制事务应该在SERVLET控制事务SERVLET中有可能调用N个服务层,他们是一个整体啊,而不是一个服务是一个整体,是多个服务层的操作是一个整体
回家再研究,这个问题大家1000%都遇到,只不过避开了事务,至少我现在的公司是这样,,就有一个DB.JAVA文件,里面有QUERY,UPDATE方法,传进的是STRING SQL参数,,根本没有使用事务,我感觉这样很不好,很危险,所以特来请教,先下,回家再继续!
con.begin
method.invoke(...)
con.commit
}catch() {
con.rollback
}如果完成一个业务逻辑需要调用到多个业务类中的方法的话,这样得使用门面模式把多个业务类
集合成为一个,将这个门面进行代理。当然了,如果你想让 Connection 出现在你的业务方法中
的话,那么动态代理这一步就可以省略掉。业务逻辑方法调用 DAO 中的 Connection 通过 ThreadLocal 得到,
VO:public class DataVo {
long dms_id; public long getDms_id() {
return this.dms_id;
} public void setDms_id(long str) {
this.dms_id = str;
} String dms_title; public String getDms_title() {
return this.dms_title;
} public void setDms_title(String str) {
this.dms_title = str;
} java.sql.Date dms_addtime; public java.sql.Date getDms_addtime() {
return this.dms_addtime;
} public void setDms_addtime(java.sql.Date str) {
this.dms_addtime = str;
} String dms_content; public String getDms_content() {
return this.dms_content;
} public void setDms_content(String str) {
this.dms_content = str;
} long nodeId; public long getNodeId() {
return this.nodeId;
} public void setNodeId(long str) {
this.nodeId = str;
}
String text;
public String getText() {
return this.text;
}
public void setText(String str){
this.text=str;
} String parm = "dms_id,dms_title,dms_addtime,dms_content,nodeId"; public String getParm() {
return parm;
} public void setParm(String parm) {
this.parm = parm;
} String parm1 = "dms_title,dms_content,nodeId"; public String getParm1() {
return parm1;
} public void setParm1(String parm1) {
this.parm1 = parm1;
} String parm2 = "dms_title=?,dms_content=?,nodeId=? where dms_id=?"; public String getParm2() {
return parm2;
} public void setParm2(String parm2) {
this.parm2 = parm2;
}
}
import java.util.List;
import java.util.Vector;import common.jiangbin.dms.DBConnection;
import common.jiangbin.dms.SplitPageCommon;
public class DataBiz {
public void insert(DataVo vo) {
DBConnection dbc = new DBConnection();
if (dbc.getConnect()) {
Connection conn = dbc.getConn();
try {
DataDao dao = new DataDao();
dao.insert(conn, vo);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("数据库连接失败!");
}
} public List findbyAll(SplitPageCommon spvo,String sqlw) {
Vector v = new Vector();
DBConnection dbc = new DBConnection();
if (dbc.getConnect()) {
Connection conn = dbc.getConn();
try {
DataDao dao = new DataDao();
v = dao.findbyAll(conn,spvo,sqlw);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("数据库连接失败!");
}
return v;
} public DataVo findbykey(DataVo vo) {
DataVo dvo = new DataVo();
DBConnection dbc = new DBConnection();
if (dbc.getConnect()) {
Connection conn = dbc.getConn();
try {
DataDao dao = new DataDao();
dvo = dao.findbykey(conn, vo);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("数据库连接失败!");
}
return dvo;
} public void update(DataVo vo) {
DBConnection dbc = new DBConnection();
if (dbc.getConnect()) {
Connection conn = dbc.getConn();
try {
DataDao dao = new DataDao();
dao.update(conn, vo);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("数据库连接失败!");
}
} public void delete(DataVo vo) {
DBConnection dbc = new DBConnection();
if (dbc.getConnect()) {
Connection conn = dbc.getConn();
try {
DataDao dao = new DataDao();
dao.delete(conn, vo);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.out.println("数据库连接失败!");
}
}
}
import java.io.PrintWriter;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import common.dms.SplitPageCommon;
public class DataServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int _act = 1;
String act = request.getParameter("act");
if (act != null && !act.equals("")) {
_act = Integer.parseInt(act);
} else {
_act = 1;
}
switch (_act) {
case 1: // 添加资料
this.insert(request, response);
break;
case 2: // 修改某条记录
this.update(request, response);
break;
case 3: // 删除某一条数据
this.delete(request, response);
break;
}
}
//1:添加资料
public void insert(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String dms_title=request.getParameter("dms_title");
String dms_content=request.getParameter("dms_content");
String nodeId=request.getParameter("nodeId");
DataVo vo=new DataVo();
vo.setDms_title(dms_title);
vo.setDms_content(dms_content);
vo.setNodeId(Long.parseLong(nodeId));
DataBiz biz=new DataBiz();
biz.insert(vo);
//request.getRequestDispatcher("DataDervlet?act=2&nodeId="+nodeId).forward(request, response);
String path = request.getContextPath();
response.sendRedirect(path+"/DataServlet?act=2&nodeId="+nodeId);
}
//2:修改某条记录
public void update(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String dms_id=request.getParameter("dms_id");
String dms_title=request.getParameter("dms_title");
String dms_content=request.getParameter("dms_content");
String nodeId=request.getParameter("nodeId");
DataVo vo=new DataVo();
vo.setDms_title(dms_title);
vo.setDms_content(dms_content);
vo.setNodeId(Long.parseLong(nodeId));
vo.setDms_id(Long.parseLong(dms_id));
DataBiz biz=new DataBiz();
biz.update(vo);
String path = request.getContextPath();
response.sendRedirect(path+"/TreeServlet?act=2&type=3&nodeId="+nodeId);
}
//3:删除某条记录
public void delete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String nodeId=request.getParameter("nodeId");
String dms_id=request.getParameter("dms_id");
DataVo vo=new DataVo();
vo.setDms_id(Long.parseLong(dms_id));
DataBiz biz=new DataBiz();
biz.delete(vo);
String path = request.getContextPath();
response.sendRedirect(path+"/TreeServlet?act=2&type=3&nodeId="+nodeId);
}
}
这个应该就是一个比较标准的纯的JDBC MVC的写法了,事业操作全都应该在BIZ层中进行
事务是以业务逻辑为基础的
一个完整的业务应该对应服务层里的一个方法
如果业务操作失败,则整个事务失败。DAO层应该保证操作的原子性,就是说DAO里的每个方法都应该是不可以分割的。所以事务是肯定没有必要加在DAO这一层的同样,对于Servlet,每一次请求对应的可能不止一个业务,打个比方,做一个删除请求,删除成功了之后肯定还要返回显示列表吧,那么这一次请求应该包含两个独立的业务逻辑。那么是不是说本来删除成功了,但在查询显示列表的时候出现在异常,就把删除的操作也回滚呢?
同样,对于Servlet,每一次请求对应的可能不止一个业务,打个比方,做一个删除请求,删除成功了之后肯定还要返回显示列表吧,那么这一次请求应该包含两个独立的业…
----这个例子举的比较牵强,,,如果是删除一个主贴呢,,那么跟 贴是必须要删除的,,也就是说,SERVLET中的所有的操作是一个整体,,,,另外Service层有可能一个业务功能需要调用N个方法,这种情况怎么办??这N个方法是一个整体,,,
ConnectionManager manager = ConnectionManager.getInstance();
try {
con = manager.getConnection();
con.setAutoCommit(false);
service1.s1();
service2.s2();
service3.s3();
con.commit;
}catch(Exception e) {
con.rollback;
}finally{
if(con!=null) {
con.close();
}
manager.removeConnection();
}ConnectionManager 采用 ThreadLocal<Connection> 对象作为 Connection 的存储容器。
getConnection() 中的大致代码(仅供参考,手工写的,没有测试过):public ConnectionManager {
private static ThreadLocal<Connection> cons = new ThreadLocal<Connection>();
private static ConnectionManger manger = new ConnectionManager(); private ConnectionManger() {
} // 不使用单例的话,把下面的这些方法改成静态的也行
public static ConnectionManager getInstance() {
return manager;
} public Connection getConnection() throws SQLException {
Connection con = cons.get();
if(con == null) {
// 从 ConnectionFactory 中获得一个连接
con = ConnectionFactory.getConnection();
setConnection(con);
}
return con;
} public void removeConnection() {
cons.remove();
} private void setConnection(Connection con) {
cons.set(con);
}
}Service 层的代码可以不用动了。DAO 的代码得进行重构:1,Connection 从 ConnectionManager 的 getConnection 中获得;
2,DAO 层中的 con.close 得全部去掉,就是不关,因为关掉的话,一个事务点就中止了;
3,DAO 层中的凡是抛出 SQLException 的地方在 catch 块中,重新包装一个自定义的 RuntimeException
再进行抛出,因为事务层放到了 Servlet 中,这里必须把异常抛出,否则在高层就没办法判断是运行正常还是
产生异常了。之所以使用 RuntimeException 主要是考虑到 service 层中不用再去修改了,如果是直接抛出
SQLException 的话,由于它是检查型异常必须处理的,这样得在 service 的每个方法后都 throws SQLException
这样的话,有点太烦了,也不是很好。DAO 重构后的代码基本上如下面这样:Connection con = null;
PreparedStatement ps = null;
try {
con = ConnectionManager.getConnection();
ps = con.prepared....;
ps.execute(....);
}catch(SQLException e) {
throw new WrapperSQLException(e);
}finally {
if(ps != null) {
try {
ps.close();
}catch(SQLException e) {
throw new WrapperSQLException(e);
}
}
// 这里的 con 不能关
}由于使用的 ThreadLocal 来存储 Connection,每个请求是独立的线程 ThreadLocal 中也只有一个 Connection
使用这个就可以将 Connection 进行传递了,而无需将 Connection 当作参数一层一层地传递下去了。另外,使用了 ThreadLocal,也不会产生线程安全的问题。如果,你的 Servlet 中完成一个业务,只是调用一个业务层的方法的话,那就更加方便了,Servlet 中的那段代码
只要使用动态代理将其切入到业务层对象中,直接调用业务类的代理对象就行了,这样就不需要让 Connection 侵入
代码中了。当然了,调用多个业务方法,采用代理进行处理的话会很麻烦,因为多个方法必须纳入到一个事务中的去,涉及事务的
传播属性,一般采用方法栈的方式进行处理,这得考虑很多的问题,类的结构需要精心设计。
ConnectionManager manager = ConnectionManager.getInstance();
try {
con = manager.getConnection();
con.setAutoCommit(false);
chain.doFilter(request, response);
con.commit;
}catch(Exception e) {
con.rollback;
}finally{
if(con!=null) {
con.close();
}
manager.removeConnection();
}
讨论一下。想想大家大多数都用 Struts + Spring + Hibernate 吧,事务处理往 Spring
的配置中一加好,就万事大吉了。估计很少有人会去研究为什么代码中一个
Connection 代码都看不到,而同样有着各种方式的事务管理呢?这个帖子的标题,用到的技术是底层的 Servlet + JDBC 与任何框架没有依赖
关系,从最底层自行实现事务管理,呵呵,难度应该不小,如果完成的话,那
就会惊叹 Spring 中的事务管理模块的处理方式。看看楼上的回复,大多数都建议在业务层中处理事务,但是我突然想到个问题。
下面是业务层中的方法:public void a() {
con.setAutoCommit;
dao.a();
b(); // 调用 b 方法
dao.a();
con.commit;
}public void b() {
con.setAutoCommit;
dao.b();
dao.b();
con.commit;
}public void c() {
con.setAutoCommit;
dao.c();
dao.c();
con.commit;
}注明一下,con.begin 和 con.commit 是通过动态代理实现的,并不是每个方法中手工加上去的。在 Servlet 中调用:service.a();
service.c();这样的话,a c 根本不能处理同一事务控制下,因为事务的边界在 Servlet 层,
并不在 Service 层中。所以在 Service 层中写事务控制语句是不对的,应该再
更高一层,Servlet 层,或者再建一个 Facade 层,把这些方法写到 Facade 层
中的一个方法中,在这个方法里处理事务。但是弄个 Facade 这样的话我们又会碰到事务的传播性这个问题,比如下面的代码:public void facade1() {
con.begin
service.a();
service.b();
facade2(); // 调用 facade2 方法
con.commit;
}public void facade2() {
con.begin
service.e();
service.f();
con.commit;
}在 facade1 中调用了 facade2 方法,注意在 facade2 中本身有事务,这时再将其
纳入了 facade1 中的事务,但根据事务的概念,facade2 在纳入 facade1 中,如果
facade1 中有事务,就加入 facade1 中的事务,自己的事务就取消了,如果没有的
话,就打开自己的事务处理,这个就是事务传播性问题。如果要从 AOP 切入事务处理的话,事务传播性是必须考虑到的问题,PS 一下,Spring
就是这么做的。如果那样做的话,事务处理模块将会变得复杂,但是这样也有好处,那就是是不依赖
于框架、平台和架构,能在 Web 程序或者是 Swing 程序中使用。当然了,如果是专门针对于 Web 的事务管理,就不要那么复杂了,直接在 Filter
中进行事务处理,每个请求,服务器都开启一个线程,这样我们就可以在线程中绑定
Connection,在 DAO 中从当前线程中获得 Connection,在 Filter 中处理事务,在
JDK 中正好提供了 ThreadLocal 这样一个类,可以在当前的线程中绑定对象,呵呵。上面是我对这个帖子事务处理的一些看法,仅仅是凭空想象的,还没实践过,在这里
仅仅作为讨论,希望大家都能参与一下。
servlet一般都是做控制层的
放到逻辑层(javabean)中
controller调用service
service中为 dao的某个或者某几个方法的组合如果不用spring 那么就用filter来实现