前言
为什么重复造轮子?
这个轮子是我去年就造了的,当时在上个东家做的是金融相关的,重视数据的一致性。我踩过一个坑(ノへ ̄、),导致我特别想知道这个spring 事务管理怎么做的。
原因一
有一次。博主我负责的一个产品其中一个业务,主要用户使用道具,银行将资金从平台转到用户账上,银行处理成功后,发送MQ消息给我们平台;博主妹子我,写了一个幂等接口,也加了@Transaction(propagation=REQUIRED),排他锁也用了。嗯,正常来说就可以了。
然鹅,我们过于信任RocketMQ了,没错就是阿里爸爸的那个,它消息堆积了,很短的时间内消费两次。我。。。(╯‵□′)╯︵┻━┻ ,导致给用户加了两次钱!!!
我沉着冷静。经过一波分析,排他锁用了,也做了幂等状态判断,按理来说应该只有一条资金流水才对,所以只有一个可能,有两条线程并发了,由于事务的传播行为是REQUIRED导致它们处在同一事务里,所以不互斥都拿到了锁。于是自己立马实验一波,...( _ _)ノ|我猜想是对的,由此说明MQ重复消费了。然后立马向我主管Tony反映了,一看还真是消息堆积了好多。。。。 后面有权限的同事修复了下MQ,而我立马把注解改成@Transaction(propagation=REQUIRES_NEW),good! 嗯,其实redis的分布式锁也可以。这是原因之一。
原因二
还是发生在上个东家的时候,技术经理喜欢分享一些技术知识。辣一次,我清楚的记得他讲了,spring是将两条线程的connection里的变量进行了复制,这样就使得两条线程的事务处在同一个connection里。听完这个,我立马就有想法要造一遍轮子。
在此之前,多线程是比较弱的一项。我造完这个轮子,我觉得我对于多线程的理解升华了。o(*  ̄▽ ̄ * )o
轮子效果
实现了添加@Transaction自动开启、提交、回滚,和REQUIRED、REQUIRES_NEW两种传播行为。就是抛出的异常处理信息还没控制。
知识点
AOP
面向切面编程,和面向对象不一样,面向对象是从类出发;而面向切面是从对象的方法出发,将对象方法看作类的组件,aop关注的就是这些组件,可以类比理解Vue的设计,将页面拆分成多个组件的思想。我对它还有个更直白的理解:aop编程就是在目标方法执行前后做某些事,比如在执行前开启事务,在执行后提交或回滚事务。
- 切面(Aspect): 切入目标业务方法前后的独立完整模块
- 连接点(Join point):具体切入的位置,即符合匹配条件的目标业务方法
- 切入点(Point Cut):切入点,定义匹配条件。
- 通知(Advice):切面的具体实现方法。
- 前置通知(Before):在目标业务方法执行前执行。
- 环绕通知(Around):在目标业务方法执行前后都会执行。
- 后置通知(AfterReturning):在目标业务方法正常执行后并且没有抛出异常执行。
- 异常通知(AfterThrowing):在目标业务方法执行抛出异常时执行。
- 最终通知(After):在目标业务方法执行后,不论是否正常执行完,最终通知都会执行。
通知执行顺序
Around->Before->Around->After->AfterReturning
事务
事务指一组sql或一条sql组合而成。
比如,A向B转账这个业务必须处在一个事务中:
1.查询A账户余额;
2.修改减少A账户余额;
3.修改添加B账号余额;
这几条sql处在同一个事务,要么一起成功,要么一起失败。
处于一个事务的前提是同一个数据库连接,jdbc调用数据库的连接是Connection接口,所以如果是同一个Connection就可以使得数据操作在同一事务。
动态代理
spring中有两种动态代理实现技术,JDK动态代理和Cglib动态代理。JDK动态代理基于接口,Cglib动态代理基于继承,都是在使用反射,在程序运行时执行。我两种都去试用了,但最后用到事务管理的是cglib。原因嘛,就是我实现完事务管理就很累了,不想再写第二遍

spring的事务传播行为
| 事务传播行为类型 | 含义 |
|---|---|
| PROPAGATION_REQUIRED | 如果线程调用的方法时没有正在运行的事务,就开启一个新事务;如果有,则加入到这个事务中 |
| PROPAGATION_SUPPORTS | 支持当前事务,如果当前方法没有处于事务中,就以非事务的方式运行 |
| PROPAGATION_MANDATORY | 使用当前事务,如果当前方法没有事务,则抛出异常 |
| PROPAGATION_REQUIRES_NEW | 每一条线程调用,都会新建一个事务 |
| PROPAGATION_NOT_SUPPORTED | 以非事务的方式执行,如果当前有事务,先挂起当前事务 |
| PROPAGATION_NEVER | 以非事务的方式运行,如果存在事务,抛出异常 |
| PROPAGATION_NESTED | 如果当前存在事务,则当前线程调起的新事务成为当前存在事务的嵌套子事务;如果不存在,新建一个事务 |
代码实现
目标
1.使用动态代理实现aop。
2.自定义一个注解@Transaction,并且有字段表示事务传播行为字段。
3.被@Transaction标记的方法自动开启事务。
4.抛出异常后,回滚事务。
5.暂时只实现REQUIRED和REQUIRES_NEW两种传播行为。
6.如果是REQUIRED,如果当前有事务,在事务结束前,后来的线程处于同一事务。
7.如果是REQUIRES_NEW,每个线程一个单独的事务。
使用到的jar包
com.springsource.net.sf.cglib-2.2.0.jar
mysql-connector-java.jar
目录结构截图

事务注解类
@Transaction.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)//注解在运行时起作用
@Target(ElementType.METHOD)//只能标记在方法上
public @interface Transcation {
/**0=>REQUIRED 1=>REQUIRES_NEW*/
int way() default 0;
}
增强对象类
import java.lang.reflect.Method;
import java.sql.Connection;
import util.ConnectionUtil;
public class TranscationManager {
public void startTransaction(Method method){
try {
System.out.println("["+Thread.currentThread().getName()+"]["+method.getName()+"] 开启事务");
ConnectionUtil.startTranscation(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void commit(Method method){
try {
System.out.println("["+Thread.currentThread().getName()+"]["+method.getName()+"] 提交事务");
ConnectionUtil.commit(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void rollback(Method method){
try {
System.out.println("["+Thread.currentThread().getName()+"]["+method.getName()+"] 回滚事务");
ConnectionUtil.rollback(method);
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用Cglib/jdk生成动态代理对象
- cglib动态代理需要的类
- 实现MethodInterceptor方法拦截器接口
- 增强对象TransactionManage
TranscationInterecptor.java
cglib中的方法拦截器
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import annoation.Transcation;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import transaction.TranscationManager;
import util.ConnectionUtil;
/**
* cglib动态代理方法拦截器
* @author Administrator
*
*/
public class TranscationInterecptor implements MethodInterceptor{
//增强对象(事务管理器)
private TranscationManager transcationManager;
public TranscationInterecptor(TranscationManager transcationManager) {
this.transcationManager=transcationManager;
}
/**
* proxy:生成动态代理对象
* method:被拦截的方法
* args:方法参数
* MethodProxy:生成的代理方法对象
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//获取方法上的@Transaction注解
Transcation annotation = method.getAnnotation(Transcation.class);
if(annotation==null){
System.out.println("不需要开启事务");
return methodProxy.invokeSuper(proxy, args);
}
//开启事务
transcationManager.startTransaction(method);
try {
//开启事务后,调用父类(目标对象)中指定方法
Object object = methodProxy.invokeSuper(proxy, args);
//提交事务
transcationManager.commit(method);
//返回父类(目标对象)返回对象
return object;
} catch (Exception e) {
//抛出异常,回滚事务
transcationManager.rollback(method);
e.printStackTrace();
}
return null;
}
}
- jdk生成动态代理需要
- InvocationHandler增强处理器接口的实现
- 目标对象
- 增强对象TransactionManager
TransactionHandler.java 增强处理器
import java.lang.reflect.Method;
import annoation.Transcation;
import net.sf.cglib.proxy.InvocationHandler;
import transaction.TranscationManager;
/**
* 增强处理器
*/
public class TransactionHandler implements InvocationHandler{
//目标对象
private Object traget;
//增强对象
private TranscationManager transcationManager;
public TransactionHandler(Object traget,TranscationManager transcationManager){
this.traget=traget;
this.transcationManager=transcationManager;
}
/**
* proxy:代理对象
* method:需要调用的方法
* args:参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Transcation annotation = method.getAnnotation(Transcation.class);
if(annotation==null){
System.out.println("不需要开启事务");
return method.invoke(traget, args);
}
//开启事务
transcationManager.startTransaction(method);
try {
//调用目标对象的方法
Object object = method.invoke(traget, args);
//提交事务
transcationManager.commit(method);
return object;
} catch (Exception e) {
//抛出异常回滚事务
transcationManager.rollback(method);
e.printStackTrace();
}
return null;
}
}
ProxyHandler.java 使用cglib/jdk生成代理类对象
import cglib.TranscationInterecptor;
import jdk.TransactionHandler;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Proxy;
import transaction.TranscationManager;
/**
* 生成cglib代理类
* @author Administrator
*
*/
public class ProxyHandler {
/** 增强对象(事务管理器) **/
private TranscationManager transcationManager=new TranscationManager();
/** 方法拦截器 **/
private TranscationInterecptor interecptor=new TranscationInterecptor(transcationManager);
/**
* 获取cglib生成的动态代理对象
*/
public Object getCglibProxy(String className) throws ClassNotFoundException{
//获取class对象
Class<?> cla = Class.forName(className);
Enhancer enhancer=new Enhancer();
//设置父类类型,即关注目标方法所属类的类型
enhancer.setSuperclass(cla);
//设置类加载器为目标类的类加载器
enhancer.setClassLoader(cla.getClassLoader());
//设置子类的回调对象
enhancer.setCallback(interecptor);
//生产代理对象,并利用类加载器加载
return enhancer.create();
}
/**
* 获取cglib生成的动态代理对象,重载方法
*/
public Object getCglibProxy(Class cla) throws ClassNotFoundException{
//获取class对象
Enhancer enhancer=new Enhancer();
//设置父类类型,即关注目标方法所属类的类型
enhancer.setSuperclass(cla);
//设置类加载器为目标类的类加载器
enhancer.setClassLoader(cla.getClassLoader());
//设置子类的回调对象
enhancer.setCallback(interecptor);
//生产代理对象,并利用类加载器加载
return enhancer.create();
}
/**
* 获取Jdk生产的动态代理对象
*/
public Object getJdkProxy(Class cla) throws Exception{
//初始化事务增强处理器
TransactionHandler transactionHandler=new TransactionHandler(cla.newInstance(), transcationManager);
//获取动态代理对象实例
Object object = Proxy.newProxyInstance(cla.getClassLoader(), cla.getInterfaces(), transactionHandler);
return object;
}
}
工具类
PropertiesUtil.java
读取工程目录下的properties文件
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.util.Properties;
public class PropertiesUtil {
//Properties键值对集合
private static Properties properties;
static{
try {
//获取当前classPath路径
String url = PropertiesUtil.class.getResource("/").toString().replace("file:/", "");
File file=new File(url);
//获取工程下所有*.properties文件
File[] files = file.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
if(name.endsWith(".properties")){
return true;
}
return false;
}
});
properties=new Properties();
for(File f:files){
Properties p=new Properties();
FileInputStream in=new FileInputStream(f);
p.load(in);
properties.putAll(p);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 根据key获取配置的value
*/
public String getValue(String key,String defaultValue){
String value = properties.getProperty(key, defaultValue);
return value;
}
}
BeanUtil.java
拷贝对象字段值到另一个对象的工具类,REQUIRED类型的传播行为需要用到,Connection之间的拷贝。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class BeanUtil {
public static void copyProperties(Object srouce,Object traget) throws Exception{
//获取class对象
Class<? extends Object> sCla = srouce.getClass();
Class<? extends Object> tCla = traget.getClass();
//获取所有的字段属性
Field[] fields = sCla.getDeclaredFields();
for(Field f:fields){
//final,static修饰的字段不拷贝
if(Modifier.isFinal(f.getModifiers())&&Modifier.isStatic(f.getModifiers())){
continue;
}
//源对象class的字段授权
f.setAccessible(true);
//获取目标对象class的同名字段
Field field = tCla.getDeclaredField(f.getName());
//授权
field.setAccessible(true);
//给目标对象字段赋值
field.set(traget, f.get(srouce));
}
}
}
ConnectionUtil.java 核心
实现事务管理,事务传播行为,jdbc操作数据的核心 ConnectionUtil做的事有:
- 获取jdbc的Connection对象
- 从一条线程的维度上看,单个线程也有可能调起多个事务,也有传播行为,比如线程1执行方法A,方法A调用方法B,两个方法都有@Transaction注解
- 开启事务
- 传播行为是REQUIRED
- 需要维护一个已经开启connection集合,connection对象地址不同,但是指向同一个数据库连接。
- 传播行为是REQUIRES_NEW
- 只维护一个给方法调用栈开启的connection就好
- 传播行为是REQUIRED
- 提交回滚事务
- 如果传播行为是REQUIRED。
- 多个connection指向的是同一个数据库连接,最后执行完的那个connection才是真正提交事务的。对于回滚,是哪条Connection所处的地方抛出异常,就是哪个回滚事务,由于指向的是同一个数据库连接,所以其他Connection指向的数据库连接也是一起回滚了的。
- 如果传播行为是REQUIRES_NEW
- 多个connection互不关联,只有异常使它们产生关联
- 如果传播行为是REQUIRED。
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import annoation.Transcation;
/**
* @author Administrator
*
*/
public class ConnectionUtil {
/**
* 单条线程方法调用链开启的Connection集合,单条线程维护一个开启Connection的栈,先开启的,最后关闭
* methodA 调用 methodB ,methodB调用methodC,从而形成ConnectionA,ConnectionB,ConnectionC
* */
private static ThreadLocal<Stack<Connection>> threadLocal=new ThreadLocal<>();
/**
* 多个线程调用同一个方法产生的connection队列
* 线程1执行方法A,产生connection1;线程2执行方法A,产生connection2
* 先开启的先执行完
* 只有当传播行为是REQUIRED才会维护该集合
* */
private static Map<Method,Deque<Connection>> openConnList=new ConcurrentHashMap<>();
private static PropertiesUtil propertiesUtil=new PropertiesUtil();
private static String url=propertiesUtil.getValue("url", null);
private static String driver=propertiesUtil.getValue("driver", null);
private static String username=propertiesUtil.getValue("username", null);
private static String password=propertiesUtil.getValue("password", null);
static{
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 重构成原型模式-深拷贝/浅拷贝 ?
* 对于多条线程在同一个事务的传播行为
* 某条线程回滚,其他线程也要回滚,从这个方向看,多条线程操作的是同一个对象--浅拷贝
* 对于多线程不在同一个事务
* 线程之间事务状态互不影响,操作的不是同一对象,可能同一时刻各自事务的状态也不同--开辟新对象新空间,不能用原型模式
* 没法实现原型模式,由于connection是mysql定义,无法使用多态
* @param method
* @return
* @throws Exception
*/
public static Connection getConnection(Method method) throws Exception{
if(method==null){
return threadLocal.get().peek();
}
//获取注解
Transcation transcation = method.getAnnotation(Transcation.class);
if(threadLocal.get()==null){
Stack<Connection> stack=new Stack<>();
threadLocal.set(stack);
}
//当前线程没有已开启的connection
if(threadLocal.get().isEmpty()){
Connection connection=DriverManager.getConnection(url, username, password);
threadLocal.get().push(connection);
}
//单条线程执行的方法再调用别的方法开启的Connection
else if(transcation.way()==0&&!threadLocal.get().isEmpty()){//被调用的方法且传播行为是0,相当于spring的REQUIRED
//同一条线程内就不需要拷贝Connection了
Connection peek = threadLocal.get().peek();
threadLocal.get().push(peek);
}else if(transcation.way()==1&&!threadLocal.get().isEmpty()){//被调用的方法且传播行为是1,相当于spring的REQUIRES_NEW
Connection connection=DriverManager.getConnection(url, username, password);
threadLocal.get().push(connection);
}
//返回栈顶元素,即最里面Connection
return threadLocal.get().peek();
}
public static void startTranscation(Method method) throws Exception{
Transcation transcation = method.getAnnotation(Transcation.class);
Connection connection=null;
if(transcation.way()==0){
//由于openConnList是静态变量需要同步锁,ConnectionUtil.class作为锁颗粒度太大,应该用openConnList+双重检查机制
synchronized (ConnectionUtil.class) {
//该方法connection队列为空
if(openConnList.get(method)==null){
Deque<Connection> value=new LinkedList<>();
openConnList.put(method, value);
}
//获取队首元素
connection = openConnList.get(method).peek();
System.out.println("["+Thread.currentThread().getName()+"] openConnFirst>>"+connection);
//新开一个connection
Connection connection2 = getConnection(method);
//当前没有其它线程在method方法上开启过事务
if(connection==null){
connection=connection2;
}else{//有线程开启过事务,将已开启的connection的值赋给新开的connection2(对于多线程)
//由于是多个线程同处一个事务,需要拷贝
BeanUtil.copyProperties(connection, connection2);
Connection pop = threadLocal.get().pop();
pop=connection2;
threadLocal.get().push(pop);
}
//入队
openConnList.get(method).offer(connection2);
}
}else{
connection = getConnection(method);
}
System.out.println("["+Thread.currentThread().getName()+"] start connection >>"+connection);
connection.setAutoCommit(false);
}
public static void commit(Method method) throws Exception{
if(threadLocal.get()==null){
return ;
}
//栈顶元素,获取当前线程最后开启的connection
Connection connection = threadLocal.get().peek();
Transcation transcation = method.getAnnotation(Transcation.class);
if(transcation.way()==0){
synchronized (ConnectionUtil.class) {
//多线程情况下,method上最后一条线程执行完才会真正提交事务
if(openConnList.get(method).size()==1){
if(!connection.isClosed()){
System.out.println("["+Thread.currentThread().getName()+"] method>>"+method.getName()+" commit>>");
if(threadLocal.get().size()==1){//对于单线程本身,最先开启connection的方法执行完才是真正提交事务(即栈底元素)
if(!connection.isClosed()){
connection.commit();
connection.close();
}
threadLocal.set(null);
}else{//单线程中,不是最先开启connection(非栈底元素)
threadLocal.get().pop();
}
}
}
//多个线程的connection出队
if(!openConnList.get(method).isEmpty()){
openConnList.get(method).pop();
}
}
}else{
if(!connection.isClosed()){
connection.commit();
connection.close();
}
if(threadLocal.get().size()>1){//非最先开启的connection
threadLocal.get().pop();//单个线程中connection出栈
}else{
threadLocal.set(null);
}
}
}
public static void rollback(Method method) throws Exception{
if(threadLocal.get()==null){
return ;
}
//取出栈顶元素
Connection connection = threadLocal.get().pop();
Transcation transcation = method.getAnnotation(Transcation.class);
if(transcation.way()==0){
synchronized (ConnectionUtil.class) {
if(!openConnList.get(method).isEmpty()){
openConnList.get(method).pop();
}
if(connection!=null&&!connection.isClosed()){
System.out.println("["+Thread.currentThread().getName()+"] rollback>>");
connection.rollback();
connection.close();
}
}
}else{
if(connection!=null&&!connection.isClosed()){
connection.rollback();
connection.close();
}
}
if(threadLocal.get().isEmpty()){
threadLocal.set(null);
}
// threadLocal.set(null);
}
public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
try {
if(rs!=null){
rs.close();
}
if(ps!=null){
ps.close();
}
if(conn!=null){
if(conn.getAutoCommit()){
conn.close();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
业务相关
User.java
public class User {
private Integer id;
private String userName;
private Integer age;
public User(){}
public User(String UserName,Integer age){
this.userName=UserName;
this.age=age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", age=" + age + "]";
}
}
UserService.java
import pojo.User;
public interface UserService {
User test(Integer id,User record,Integer mark) throws Exception;
void insert() throws Exception;
}
UserServiceImpl.java
import annoation.Transcation;
import dao.UserDAO;
import pojo.User;
import proxy.ProxyHandler;
public class UserServiceImpl implements UserService{
private UserDAO userDAO=new UserDAO();
ProxyHandler handler=new ProxyHandler();
/**
* REQUIRES_NEW
* @param id
* @param record
* @return
* @throws Exception
*/
@Transcation(way=0)
public User test(Integer id,User record,Integer mark) throws Exception {
System.out.println("sql执行中");
User user = userDAO.selectById(id);
userDAO.insert(new User(record.getUserName()+"_method-test",25));
System.out.println("["+Thread.currentThread().getName()+"] user>>"+user);
UserServiceImpl userService = (UserServiceImpl) handler.getCglibProxy(UserServiceImpl.class);
userService.insert(record,mark);
System.out.println("["+Thread.currentThread().getName()+"] 插入数据主键>>"+user.getId());
return user;
}
/**
* REQUIRED
* @param user
* @param mark
* @throws Exception
*/
@Transcation(way=0)
public void insert(User user,Integer mark) throws Exception{
String userName = user.getUserName();
user.setUserName(userName+"_method-insert");
userDAO.insert(user);
if(mark==1){
throw new Exception("线程1抛异常");
}
}
@Override
@Transcation
public void insert() throws Exception {
userDAO.insert(new User("ttttt",22));
int s=1/0;
}
}
UserDAO.java
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import pojo.User;
import util.ConnectionUtil;
public class UserDAO {
public User selectById(Integer id) throws Exception{
Connection connection = ConnectionUtil.getConnection(null);
PreparedStatement prepareStatement = connection.prepareStatement("select * from user where id=? for update");
prepareStatement.setInt(1, id);
ResultSet resultSet = prepareStatement.executeQuery();
User user=new User();
if(resultSet.next()){
user.setId(resultSet.getInt("id"));
user.setUserName(resultSet.getString("user_name"));
user.setAge(resultSet.getInt("age"));
}
/*prepareStatement = connection.prepareStatement("insert into user (user_name,age) values(?,?)");
prepareStatement.setString(1,new String((Thread.currentThread().getName()+" selectById").getBytes(),"utf-8"));
prepareStatement.setInt(2, 22);
int row = prepareStatement.executeUpdate();*/
ConnectionUtil.close(resultSet, prepareStatement, connection);
return user;
}
public User insert(User user) throws Exception{
Connection connection = ConnectionUtil.getConnection(null);
PreparedStatement prepareStatement = connection.prepareStatement("insert into user (user_name,age) values(?,?)");
prepareStatement.setString(1,new String((user.getUserName()+" insert").getBytes(),"utf-8"));
prepareStatement.setInt(2, user.getAge());
int row = prepareStatement.executeUpdate();
ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
if(generatedKeys.next()){
user.setId(generatedKeys.getInt(1));
}
return user;
}
}
client.java 测试
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import pojo.User;
import util.ConnectionUtil;
public class UserDAO {
public User selectById(Integer id) throws Exception{
Connection connection = ConnectionUtil.getConnection(null);
PreparedStatement prepareStatement = connection.prepareStatement("select * from user where id=? for update");
prepareStatement.setInt(1, id);
ResultSet resultSet = prepareStatement.executeQuery();
User user=new User();
if(resultSet.next()){
user.setId(resultSet.getInt("id"));
user.setUserName(resultSet.getString("user_name"));
user.setAge(resultSet.getInt("age"));
}
/*prepareStatement = connection.prepareStatement("insert into user (user_name,age) values(?,?)");
prepareStatement.setString(1,new String((Thread.currentThread().getName()+" selectById").getBytes(),"utf-8"));
prepareStatement.setInt(2, 22);
int row = prepareStatement.executeUpdate();*/
ConnectionUtil.close(resultSet, prepareStatement, connection);
return user;
}
public User insert(User user) throws Exception{
Connection connection = ConnectionUtil.getConnection(null);
PreparedStatement prepareStatement = connection.prepareStatement("insert into user (user_name,age) values(?,?)");
prepareStatement.setString(1,new String((user.getUserName()+" insert").getBytes(),"utf-8"));
prepareStatement.setInt(2, user.getAge());
int row = prepareStatement.executeUpdate();
ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
if(generatedKeys.next()){
user.setId(generatedKeys.getInt(1));
}
return user;
}
}