终于学完了数据库的连接,可以做一个完整的项目了,以前做的练习都没有关联到数据库,没法进行事务。
先上图:
画的图,有点乱,但是大概意思还是可以理解。
这个练习是简单的存储一个学生读了哪些书,存进数据库中,当然也可以查询。
主页图:
代码奉上:
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
学生图书信息管理
学生图书信息查询
只是随便转转,不要开启事务
index请求的servlet.java
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.IStudServiceImpl;
import cn.hncu.utils.TxProxy;
public class StudServlet extends HttpServlet {
// IStudService service=new IStudServiceImpl();//这是直接在service层中开启事务,也是version 1
// IStudService service=TxProxy.getProxy(IStudServiceImpl.class);//version 2
IStudService service=TxProxy.getProxy(new IStudServiceImpl());//version 3
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String cmd=request.getParameter("cmd");
if("query".equals(cmd)){//不直接用cmd去判断,是为了防止cmd为null,更安全些
query(request, response);
}else if("save".equals(cmd)){
save(request, response);
}else if("abc".equals(cmd)){
abc(request,response);
}
}
private void query(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List
servlet要组织和封装从前台页面发来的数据,这里用了两个值对象:
Stud.java
package cn.hncu.domain;
import java.util.ArrayList;
import java.util.List;
/*
* 一对多中“一方”值对象的建法
*/
public class Stud {
private String id;
private String name;
//为多方开一个集合,用来体现多表中“一对多”的关系
private List books=new ArrayList();//注意:该集合要在类构造或之前就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 getBook() {
return books;
}
public void setBook(List book) {
this.books = book;
}
@Override
public String toString() {
return "Stud [id=" + id + ", name=" + name + ", book=" + books + "]";
}
}
Book.java
package cn.hncu.domain;
/*
* 一对多中的“多”方值对象的建法
*/
public class Book {
private Integer id;//基本数据类型全用包装类来声明,为以后使用框架做技术准备----包装类能够兼容框架(因为一般框架都会使用类反射)
private String name;
private Double price;
//专为“一”方添加一个对象类型的变量(不直接使用String studid)-----体现多表中的“一对多”关系
private Stud stud=new Stud();//设置书的主人
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 getStud() {
return stud;
}
public void setStud(Stud stud) {
this.stud = stud;
}
//多表关联时toString()有一个陷阱:不要出现一方输出另外一方,另外一方又输出这一方的情况,形成无穷循环
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
servlet调用service,service被动态代理的TxProxy.java
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
public class TxProxy implements InvocationHandler {
private Object srcObj=null;
private TxProxy(Object srcObj) {
this.srcObj=srcObj;
}
/*version 1
public static Object getProxy(Object srcObj){
Object newObj=Proxy.newProxyInstance(
TxProxy.class.getClassLoader(),
srcObj.getClass().getInterfaces(),
new TxProxy(srcObj));
return newObj;
}
*/
/*version 2
public static T getProxy(Class c){
Object obj=null;
try {
obj = c.newInstance();
} catch (Exception e) {
throw new RuntimeException(c+"没有空参方法!");
}
Object newObj=Proxy.newProxyInstance(
TxProxy.class.getClassLoader(),
obj.getClass().getInterfaces(),
new TxProxy(obj));
return (T)newObj;
}
*/
public static T getProxy(T srcObj){
Object newObj=Proxy.newProxyInstance(
TxProxy.class.getClassLoader(),
srcObj.getClass().getInterfaces(),
new TxProxy(srcObj));
return (T)newObj;
}
/*version 1与version 2公用
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Connection con=null;
Object returnObj=null;
try {
con=ConnUtils5.getConn();
con.setAutoCommit(false);
System.out.println("开启事务.........");
returnObj=method.invoke(srcObj, args);//放行
con.commit();//
System.out.println("提交事务.......");
} catch (Exception e) {
con.rollback();
System.out.println("回滚事务.........");
}finally{
try {
con.setAutoCommit(true);
con.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return returnObj;
}
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.isAnnotationPresent(Transaction.class)){
System.out.println("此方法存在注解,进行拦截......");
Connection con=null;
Object returnObj=null;
try {
con=ConnUtils5.getConn();
con.setAutoCommit(false);
System.out.println("开启事务.........");
returnObj=method.invoke(srcObj, args);//放行
con.commit();//
System.out.println("提交事务.......");
} catch (Exception e) {
con.rollback();
System.out.println("回滚事务.........");
}finally{
try {
con.setAutoCommit(true);
con.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return returnObj;
}
System.out.println("没有注解,直接放行....");
return method.invoke(srcObj, args);
}
}
service层的接口IStudService.java
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 abstract List
接口用到的注解:
Transaction.java
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 {
}
实现类IStudServiceImpl.java
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 IStudServiceImpl implements IStudService{
public IStudServiceImpl() {
}
StudDAO stuDao=new StudJdbcDao();
BookDAO booDao=new BookJdbcDao();
@Override
public List
service调用dao层,由于要一个表要对应一个dao,这里有stud表和book表,所以有两个dao
接口StudDAO.java
package cn.hncu.stud.dao;
import java.util.List;
import java.util.Map;
import cn.hncu.domain.Stud;
public interface StudDAO {
public abstract List
实现类StudJdbcDao.java
package cn.hncu.stud.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import cn.hncu.domain.Stud;
import cn.hncu.utils.ConnUtils5;
public class StudJdbcDao implements StudDAO{
@Override
public List
接口BookDAO.java
package cn.hncu.stud.dao;
import java.util.List;
import cn.hncu.domain.Book;
public interface BookDAO {
public abstract void save(List list) throws Exception;
}
实现类BookJdbcDao.java
package cn.hncu.stud.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import cn.hncu.domain.Book;
import cn.hncu.utils.ConnUtils5;
public class BookJdbcDao implements BookDAO{
@Override
public void save(List list) throws Exception {
Connection con=ConnUtils5.getConn();
String sql="insert into book(name,price,studid) values(?,?,?)";
// INSERT INTO book(NAME,price,studid) VALUES("aa",33.5,"1");
PreparedStatement pst=con.prepareStatement(sql);
for(Book book:list){
pst.setString(1, book.getName());
pst.setDouble(2, book.getPrice());
pst.setString(3, book.getStud().getId());
pst.addBatch();//此处不要参数!!!!!添加到批处理(有很多条sql语句要执行时,用批处理)
}
pst.executeBatch();//执行批处理
}
}
类和包:
存储到数据库:
学生表:
图书表:
查询:
在项目中又有两个新的知识点:
java.lang.ThreadLocal
这个类本质上就是里面维护着一个HashMap
即:同一个线程拿同一个对象,不同线程则是不同对象。
这里做了一些练习:
ThreadLocalUtil.java
package cn.hncu.threadLocDemo;
import java.util.Random;
public class ThreadLocalUtil {
// private static ThreadLocal
测试类Client.java
package cn.hncu.threadLocDemo;
import org.junit.Test;
public class Client {
@Test
public void test1(){
Object o1=ThreadLocalUtil.getObj();
Object o2=ThreadLocalUtil.getObj();
System.out.println("o1: "+o1+",o2: "+o2);
System.out.println("o1==o2? "+(o1==o2));
}
@Test
public void test2(){
test1();
System.out.println();
Object o3=ThreadLocalUtil.getObj();
Object o4=ThreadLocalUtil.getObj();
System.out.println("o3: "+o3+",o4: "+o4);
System.out.println("o3==o4? "+(o3==o4));
System.out.println();
new Thread(){
@Override
public void run() {
Object o4=ThreadLocalUtil.getObj();
Object o5=ThreadLocalUtil.getObj();
System.out.println("o4: "+o4+",o5: "+o5);
System.out.println("o4==o5? "+(o4==o5));
System.out.println();
}
}.start();
}
/*
* 测试结果:obj1,obj2,obj3,obj4由于是同一个线程,所以obj也都相同,
* obj5和obj6两者相同,但是他们是另外一个线程获取的,所以和obj1`obj4是不相等的
*/
}
测试图:
在知道ThreadLocal的功能后,我们自己模拟了一下它的功能:
MyThreadLocal.java
package cn.hncu.threadLocDemo;
import java.util.HashMap;
import java.util.Map;
/*
* 自己模拟ThreadLocal功能,便于理解
* 虽然表面的功能能够模拟出来,但是ThreadLocal真正的功能远远比这强,比如内存管理
*/
public class MyThreadLocal {
private Map map=new HashMap();
public Object get(){
Thread t=Thread.currentThread();
return map.get(t);
}
public void set(Object obj){
map.put(Thread.currentThread(), obj);
}
}
同样可以得出一样的结果,但是仅仅只是模拟了表面功能!
前面已经贴出代码,但是这里用到了类反射、注解、泛型和动态代理,这几个知识点不是很熟,所以在这里再写遍,提醒一下自己!
TxProxy.java
package cn.hncu.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
public class TxProxy implements InvocationHandler {
private Object srcObj=null;
private TxProxy(Object srcObj) {
this.srcObj=srcObj;
}
public static T getProxy(T srcObj){
Object newObj=Proxy.newProxyInstance(
TxProxy.class.getClassLoader(),
srcObj.getClass().getInterfaces(),
new TxProxy(srcObj));
return (T)newObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.isAnnotationPresent(Transaction.class)){
System.out.println("此方法存在注解,进行拦截......");
Connection con=null;
Object returnObj=null;
try {
con=ConnUtils5.getConn();
con.setAutoCommit(false);
System.out.println("开启事务.........");
returnObj=method.invoke(srcObj, args);//放行
con.commit();//
System.out.println("提交事务.......");
} catch (Exception e) {
con.rollback();
System.out.println("回滚事务.........");
}finally{
try {
con.setAutoCommit(true);
con.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return returnObj;
}
System.out.println("没有注解,直接放行....");
return method.invoke(srcObj, args);
}
}