本例的学生信息添加进入数据库的事务(可以提交事务,事务回滚,用本地线程完善)
主页面index.jsp
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri=" http://java.sun.com/jsp/jstl/core " prefix="c" %>
< html >
< head >
< title >学生信息管理</ title >
</ head >
< body >
< a href='<c:url value = "/query" />?cmd=query'>查看学生信息</ a >
< br >< br >
<!--
<a href="<c:url value='/StudServlet?cmd=save' />">学生信息添加</a>
-->
< h2 >学生信息添加</ h2 >
< form action='<c:url value = "/query" />?cmd=add' method="post">
姓名:< input type = "text" name = "name" />< br >< br >
< fieldset style = "border: solid;border-color: red;width: 250px;" >
< legend >图书1</ legend >
书名:< input type = "text" name = "book" />< br >< br >
价格:< input type = "text" name = "price" />
</ fieldset >
< br >
< fieldset style = "border: solid;border-color:green;width: 250px;" >
< legend >图书2</ legend >
书名:< input type = "text" name = "book" />< br >< br >
价格:< input type = "text" name = "price" />
</ fieldset >
< br >< br >
< input type = "submit" value = "提交" />< br >< br >
</ form >
</ body >
</ html >
|
工具包
获取数据库连接的工具ConnUtils5.java
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class ConnUtils5 {
//本地线程管理对象,用于实现: 同一个线程获取的连接是同一个
private static ThreadLocal< Connection> t= new ThreadLocal<Connection>();
private final static List<Connection> pool= new ArrayList<Connection>();
private static int SIZE; //由资源文件读取
private ConnUtils5(){
}
static {
Properties p= new Properties();
try {
//下面这种方式在纯Java项目中可以读取到classpath下的资源文件,但无法读取JavaEE项目的。因为Tomcat把系统的默认类加载器改了
//p.load( ClassLoader.getSystemClassLoader().getSystemResourceAsStream("jdbc.properties"));
// p.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));
//读取Web项目的classpath下的资源文件,用这个可以
p.load(ConnUtils3. class .getClassLoader().getResourceAsStream( "jdbc.properties" ));
String driver=p.getProperty( "driver" );
String url=p.getProperty( "url" );
String name=p.getProperty( "username" );
String pwd=p.getProperty( "password" );
String ssize=p.getProperty( "size" );
SIZE=Integer.parseInt(ssize);
Class.forName(driver);
for ( int i= 0 ;i<SIZE;i++){
final Connection con=DriverManager.getConnection(url,name,pwd);
System.out.println( "con==" +con);
//更改conn.close()方法
//用代理模式生成一个增强版的conn对象,把它的close()方法拦截更改掉
Object nCon=Proxy.newProxyInstance(
ConnUtils3. class .getClassLoader(),
// conn.getClass().getInterfaces(),
//后面这种方式不行,应该是驱动中的实现类和我们当前程序不在同一空间(类加载器不同)
new Class[]{Connection. class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals( "close" )){
System.out.println( "还回一个链接:" +(Connection)proxy);
pool.add((Connection)proxy);
return null ;
}
return method.invoke(con, args);
}
});
pool.add((Connection)nCon);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static synchronized Connection getConnection(){
//先从t中拿,如果有就拿出去,如果没有再到池中拿且把该对象放到t中
Connection con=t.get();
if (con== null ){
if (pool.size()<= 0 ){
System.out.println( "池中连接没有了..." );
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return getConnection();
}
con=pool.remove( 0 );
t.set(con); //放到t中
}
return con; //拿一个移一个
}
}
|
代理
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
public class TxProxy implements InvocationHandler {
private Object srcObj= null ;
private TxProxy(Object srcObj) {
this .srcObj = srcObj;
}
public static Object getProxy(Object srcObj){
System.out.println( "srcObj:" +srcObj);
Object newObj=Proxy.newProxyInstance(
TxProxy. class .getClassLoader(),
srcObj.getClass().getInterfaces(),
new TxProxy(srcObj));
System.out.println( "newObj:" +newObj);
return newObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Connection con= null ;
Object returnObj= null ;
try {
con=ConnUtils5.getConnection();
System.out.println( "invoke拿到一个链接:" +con);
con.setAutoCommit( false );
returnObj=method.invoke(srcObj, args);
System.out.println( "提交一个事务..." );
con.commit();
} catch (Exception e) {
try {
System.out.println( "回滚一个事务..." );
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
con.setAutoCommit( true );
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return returnObj;
}
}
|
代理2:不需要强转,但是代理了所有
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
public class TxProxy2 implements InvocationHandler {
private Object srcObj= null ;
private TxProxy2(Object srcObj) {
this .srcObj = srcObj;
}
public static <T> T getProxy(Class<T> c){
Object obj= null ;
try {
obj = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
Object newObj=Proxy.newProxyInstance(
TxProxy2. class .getClassLoader(),
c.getInterfaces(),
new TxProxy2(obj));
return (T) newObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Connection con= null ;
Object returnObj= null ;
try {
con=ConnUtils5.getConnection();
System.out.println( "invoke拿到一个链接:" +con);
con.setAutoCommit( false );
returnObj=method.invoke(srcObj, args);
System.out.println( "提交一个事务..." );
con.commit();
} catch (Exception e) {
try {
System.out.println( "回滚一个事务..." );
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
con.setAutoCommit( true );
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return returnObj;
}
}
|
注解
1
|
2
3
4
5
6
7
8
9
10
11
12
|
package cn.hncu.utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target (value=ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface Transaction {
}
|
代理3:用注解实现需要事务则用事务
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
public class TxProxy3 implements InvocationHandler {
private Object srcObj= null ;
private TxProxy3(Object srcObj) {
this .srcObj = srcObj;
}
public static <T> T getProxy(T srcObj){
Object newObj=Proxy.newProxyInstance(
TxProxy3. class .getClassLoader(),
srcObj.getClass().getInterfaces(),
new TxProxy3(srcObj));
return (T) newObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/* 这种方式来实现只拦截指定的方法
if(method.getName().equals("close")){
...拦截
}else{
return method.invoke(srcObj, args);
}
*/
if (method.isAnnotationPresent(Transaction. class )){
Connection con= null ;
Object returnObj= null ;
try {
con=ConnUtils5.getConnection();
System.out.println( "invoke拿到一个链接:" +con);
con.setAutoCommit( false );
//真正的业务代码,放行
returnObj=method.invoke(srcObj, args);
System.out.println( "提交一个事务..." );
con.commit();
} catch (Exception e) {
try {
System.out.println( "回滚一个事务..." );
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
con.setAutoCommit( true );
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return returnObj;
} else {
System.out.println( "不存在事务注解,直接放行!" );
return method.invoke(srcObj, args);
}
}
}
|
资源文件jdbc.properties
1
|
2
3
4
5
6
7
8
9
10
11
|
##MySQL
driver=com.mysql.jdbc.Driver
url=jdbc: mysql://127.0.0.1:3306/hncu?useUnicode= true &characterEncoding=utf-8
username=root
password =1234
size =3
##Oracle
#driver=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
#username=scott
# password =tiger
|
stud层的servlet层–QueryServlet.java
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
package cn.hncu.stud.servlet;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.hncu.domain.Book;
import cn.hncu.domain.Stud;
import cn.hncu.stud.service.IStudService;
import cn.hncu.stud.service.StudServiceImpl;
import cn.hncu.utils.TxProxy3;
public class QueryServlet extends HttpServlet {
//注入
//1.
// IStudService service=(IStudService) TxProxy.getProxy(new StudServiceImpl());
//2.
// IStudService service=TxProxy2.getProxy(StudServiceImpl.class);
//3.
IStudService service=TxProxy3.getProxy( new StudServiceImpl());
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String cmd=request.getParameter( "cmd" );
System.out.println( "cmd:" +cmd);
if ( "query" .equals(cmd)){
query(request, response);
} else if ( "add" .equals(cmd)){
add(request, response);
}
}
public void query(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println( "service:" +service);
List<Map<String, String>> studs=service.query();
request.setAttribute( "studs" , studs);
request.getRequestDispatcher( "/jsps/show.jsp" ).forward(request, response);
}
public void add(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1收集参数 2组织参数(id字段留到dao中去补)
String name[]=request.getParameterValues( "name" );
Stud s= new Stud();
s.setName(name[ 0 ]);
//图书信息
String books[]=request.getParameterValues( "book" );
//防护一下 ---价格的防护应该也要写,这里我们偷懒了
if (books== null ||books.length<= 0 ){
return ;
}
String prices[]=request.getParameterValues( "price" );
for ( int i= 0 ;i<books.length;i++){
Book b= new Book();
b.setName(books[i]);
b.setPrice(Double.parseDouble(prices[i]));
//※完成两个值对象的“一对多”关系的数据封装
s.getBooks().add(b); //一方
b.setS(s); //多方
}
//3调用service层
try {
service.save(s);
} catch (Exception e) {
//导向失败页面
}
}
}
|
stud层的service层–
接口:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package cn.hncu.stud.service;
import java.util.List;
import java.util.Map;
import cn.hncu.domain.Stud;
import cn.hncu.utils.Transaction;
public interface IStudService {
public List<Map<String, String>> query();
//注意,注解只有放在接口才有用,,,,写在实现类中的方法无效(不会决定开启事务)
@Transaction
public void save(Stud stud) throws Exception ;
}
|
实现类
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package cn.hncu.stud.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import cn.hncu.domain.Stud;
import cn.hncu.stud.dao.BookDAO;
import cn.hncu.stud.dao.BookJdbcDao;
import cn.hncu.stud.dao.StudDAO;
import cn.hncu.stud.dao.StudJdbcDAO;
import cn.hncu.utils.ConnUtils5;
/*我们以后开发时通常都要采用一个dao独立操作一个表,系统中有几个实体表就写几个dao,
* 以后框架都是这么干的,我们也要这样做,因为架构好!
*
* 采用事务的场合:
* 1、如果只有一个dao,但要执行多条sql语句且涉及增删改,则要开启事务
* 2、如果一个service调用多个dao,通常也要开启事务。
*/
public class StudServiceImpl implements IStudService {
//注入
StudDAO dao_stud= new StudJdbcDAO();
BookDAO dao_book= new BookJdbcDao();
@Override
public List<Map<String, String>> query() {
return dao_stud.query();
}
@Override
public void save(Stud stud) throws Exception {
dao_stud.save(stud);
dao_book.save(stud.getBooks());
}
}
|
stud层的dao层–
Stud接口–分离式做法,一个表对应一个dao,为框架做准备
1
|
2
3
4
5
6
7
8
9
10
11
12
|
package cn.hncu.stud.dao;
import java.util.List;
import java.util.Map;
import cn.hncu.domain.Stud;
public interface StudDAO {
public List<Map<String, String>> query();
public void save(Stud stud) throws Exception;
}
|
Stud实现类
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package cn.hncu.stud.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import cn.hncu.domain.Book;
import cn.hncu.domain.Stud;
import cn.hncu.utils.ConnUtils3;
public class StudJdbcDAO implements StudDAO {
@Override
public List<Map<String, String>> query() {
List<Map<String, String>> list= new ArrayList<Map<String,String>>();
//一个map就是一行数据, List<Map>就是整个数据表
Connection con= null ;
try {
con=ConnUtils3.getConnection();
Statement st=con.createStatement();
String sql= "select * from stud" ;
ResultSet rs=st.executeQuery(sql);
while (rs.next()){
Map<String,String> m= new HashMap<String, String>();
m.put( "id" , (String) rs.getObject( 1 ));
m.put( "name" , (String) rs.getObject( 2 ));
list.add(m);
}
rs.close();
st.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return list;
}
@Override
public void save(Stud stud) throws Exception {
Connection con=ConnUtils3.getConnection();
System.out.println( "拿到一个链接:" +con);
String sql= "insert into stud values(?,?)" ;
String uuid=UUID.randomUUID().toString().replace( "-" , "" );
PreparedStatement pst=con.prepareStatement(sql);
stud.setId(uuid); //为了"多方"即book能够拿到"一方"的id,专门补的
pst.setString( 1 , uuid);
pst.setString( 2 , stud.getName());
System.out.println( "1:" +uuid+ ",2:" +stud.getName());
pst.executeUpdate();
// con.close();//拿到同一个con,这里就不需要关了
}
}
|
Book接口
1
|
2
3
4
5
6
7
8
9
10
|
package cn.hncu.stud.dao;
import java.util.List;
import cn.hncu.domain.Book;
public interface BookDAO {
public void save(List<Book> books) throws Exception;
}
|
Book实现类
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package cn.hncu.stud.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.List;
import cn.hncu.domain.Book;
import cn.hncu.utils.ConnUtils3;
public class BookJdbcDao implements BookDAO {
@Override
public void save(List<Book> books) throws Exception {
Connection con=ConnUtils3.getConnection();
System.out.println( "拿到一个链接:" +con);
String sql= "insert into book(name,price,studid) values(?,?,?)" ;
PreparedStatement pst=con.prepareStatement(sql);
for (Book b:books){
pst.setString( 1 , b.getName());
pst.setDouble( 2 , b.getPrice());
pst.setObject( 3 , "12132312" ); //异常(故意给一个不存在的外键字段,以测试事务回滚)--测事务回滚
// pst.setObject(3, b.getS().getId());
System.out.println( "1:" +b.getName()+ ",2:" +b.getPrice()+ ",3:" +b.getS().getId());
pst.addBatch(); //添加到批处理
}
pst.executeBatch(); //执行批处理
// con.close();//这里拿到同一个con,这里不需要关
}
}
|
值对象
Stud对象
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package cn.hncu.domain;
import java.util.ArrayList;
import java.util.List;
/*
* 一对多中的 “一”方 值对象的建法
*/
public class Stud {
private String id;
private String name;
//※专为“多”方添加一个集合---体现多表中的“一对多关系”
private List<Book> books= new ArrayList<Book>(); //注意,该集合要在构造时或之前就new出来。
public String getId() {
return id;
}
public void setId(String id) {
this .id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this .books = books;
}
@Override
public String toString() {
return "id=" + id + "," + name + "," + books;
}
}
|
Book对象
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package cn.hncu.domain;
/*
* 一对多中的 “多”方 值对象的建法
*/
public class Book {
private Integer id;
//基本数据类型全部用包装类的声明,为以后使用框架做技术准备---包装类能够兼容框架(因为一般框架都会使用类反射)
private String name;
private Double price;
//※专为“一”方添加一个对象类型的变量(注意,不用studid)---体现多表中的“一对多关系”
private Stud s;//设置主人
//private String studid;//★★不要这样设
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Stud getS() {
return s;
}
public void setS(Stud s) {
this.s = s;
}
/*
* 多表关联时的toString()方法要注意一个陷阱,就是一方输出另一方,同时另一方又反过来输出前一方,形成无穷递归!
*/
@Override
public String toString() {
return "id=" + id + "," + name + "," + price; //这里不能输出Stud对象,否则无穷递归
}
}
|
显示学生信息页面jsps/show.jsp
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri=" http://java.sun.com/jsp/jstl/core " prefix="c" %>
< html >
< head >
< title >学生信息管理</ title >
</ head >
< body >
< h2 >学生信息</ h2 >
< c:forEach items = "${studs}" var = "x" >
${x.id},${x.name}< br />
</ c:forEach >
</ body >
</ html >
|
效果图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。