使用jdbc+注解+反射+动态代理做Java应用中带事务管理的操作数据库方法

总目标:使用jdbc+注解+反射+动态代理做Java应用中带事务管理的操作数据库方法
    需求:
    (1)在数据库中对数据的增删改操作,为了保证数据的安全,需要加事务
    (2)应用中包含很多模块,而模块中一般包含最基础单表的增删改,如果每个单表的增删改方法都要写事务管理的话,代码太冗余了,可以统一处理
    (3)增删改的事务管理可以扩展到一个总方法(包含多个增删改,事务本来也是针对一组SQL执行的最终结果)

1、步骤:
    先看文件结构(图中红框)

    

1.1 新建maven项目,添加jdbc、连接池依赖

<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.23</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.6</version>
		</dependency>

1.2 配置文件及配置文件读取工具类

配置文件:jdbc.properties,数据库这里用的是MySQL,记得在URL中改自己的数据库名称

jdbc.pool.user=root
jdbc.pool.password=123456
jdbc.pool.url=jdbc:mysql://127.0.0.1:3306/数据库名称?useSSL=true
jdbc.pool.driverName=com.mysql.cj.jdbc.Driver
jdbc.pool.initialSize=3
jdbc.pool.maxActive=10
jdbc.pool.maxWait=5000

配置文件内容读取类:ConfigBuilder.java //自己加上get()方法

package com.my.zonghe.configBuild;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ConfigBuilder {
	static String url;
	static String user;
	static String password;
	static String driverName;
	static Integer initialSize;
	static Integer maxActive;
	static Integer maxWait;
	static {
		//1、读取配置文件,路径从target/classes下开始
		String path = ConfigBuilder.class.getClassLoader().
						getResource("com/my/zonghe/config/jdbc.properties").getPath();
		//2.使用properties读取内容
		Properties ps = new Properties();
		InputStream in = null;
		try {
			in = new FileInputStream(new File(path));
			ps.load(in);
			url = ps.getProperty("jdbc.pool.url");
			user = ps.getProperty("jdbc.pool.user");
			password = ps.getProperty("jdbc.pool.password");
			driverName = ps.getProperty("jdbc.pool.driverName");
			initialSize = Integer.valueOf(ps.getProperty("jdbc.pool.initialSize"));
			maxActive = Integer.valueOf(ps.getProperty("jdbc.pool.maxActive"));
			maxWait = Integer.valueOf(ps.getProperty("jdbc.pool.maxWait"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
//自己加上get()方法吧
}

1.3 某业务模块,这里以dept单表为例。由于核心代码不多,直接截图吧

简单说明:

(1)数据库表dept,这个表只有三个字段,其中deptno为主键,并且自增,其他为字符类型。以该表在entity包下建实体对象Dept。

(2)DeptDaoImpl继承了BaseConnection,实现了DeptDao,具体方法在DeptDaoImpl中写,只需要写sql串和SQL执行。事务不在这里处理,到代理类中统一处理

(3)BaseConnection定义了数据库连接,所有想使用数据库连接的,直接继承该类即可,BaseConnection中代码如下

1.4 数据库连接次、代理类等基础模块

(1)BaseDao.java 数据库连接池(数据源)

package com.my.zonghe.Base;

import org.apache.commons.dbcp.BasicDataSource;
import com.my.zonghe.configBuild.ConfigBuilder;

public class BaseDao {
	//1、
	private static BasicDataSource basicDataSource;
	public static BasicDataSource getBasicDataSource() {
		if(basicDataSource == null) {
			basicDataSource = new BasicDataSource();
			basicDataSource.setUsername(ConfigBuilder.getUser());
			basicDataSource.setPassword(ConfigBuilder.getPassword());
			basicDataSource.setUrl(ConfigBuilder.getUrl());
			basicDataSource.setDriverClassName(ConfigBuilder.getDriverName());
			//3、设置连接池使用配置,包括初始化数量、最大连接数、最小空闲数、最大等待时间
			basicDataSource.setInitialSize(ConfigBuilder.getInitialSize());
			basicDataSource.setMaxActive(ConfigBuilder.getMaxActive());
			//从连接池中获取连接的最大等待时间
			basicDataSource.setMaxWait(ConfigBuilder.getMaxWait());
		}
		return basicDataSource;
	}
}

(2)BaseConnection.java 在上面已经说明

(3)DaoHandler.java代理类,对包含数据库操作方法(通过注解进行识别)加上事务管理。在代码中加了一些打印,直观看到执行

package com.my.zonghe.Base;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

import com.my.zonghe.myUtil.Transaction;

public class DaoHandler implements InvocationHandler{

	private Object targetObject;
	
	public Object newProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
    	Object newProxyInstance = Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), 
    			this.targetObject.getClass().getInterfaces(), this);
		//获得动态代理对象
    	return newProxyInstance;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object obj = null;
		if(method.getAnnotationsByType(Transaction.class).length > 0) {
			//有注解
			System.out.println("提交前");
			Field field = targetObject.getClass().getSuperclass().getDeclaredField("conn");
			field.setAccessible(true);
			
			System.out.println(field.get(targetObject));
			Connection conn = (Connection)field.get(targetObject);
			conn.setAutoCommit(false);
			try {
				obj = method.invoke(targetObject, args);
				conn.commit();
				System.out.println("提交了");
			} catch (Exception e) {
				e.printStackTrace();
				conn.rollback();
				System.out.println("回滚了");
			} finally {
				if(conn != null) {
					conn.close();
					System.out.println("关闭连接了");
				}
			}
		}else {
			//不使用事务也得关闭连接
			obj = method.invoke(targetObject, args);
			Field field =         targetObject.getClass().getSuperclass().getDeclaredField("conn");
			if(field != null) {
				field.setAccessible(true);
				Connection conn = (Connection)field.get(targetObject);
				if(conn != null) {
					conn.close();
					System.out.println("关闭连接了");
				}
			}
		}
		return obj;
	}
}

(4)事务管理注解

Transaction.java,在需要事务管理的方法上加注解,如本文DeptDao.java方法(获取代理类时,用父类引用指向子类对象,加在接口DeptDao.java上即可)

package com.my.zonghe.Base;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transaction {
	public String value() default "";
}

2、测试TestDemo

代码:

package com.my.zonghe.test;

import com.my.zonghe.Base.DaoHandler;
import com.my.zonghe.dept.dao.DeptDao;
import com.my.zonghe.dept.dao.impl.DeptDaoImpl;
import com.my.zonghe.dept.entity.Dept;

public class TestDemo {
	public static void main(String[] args) {
		Dept dept = new Dept();
		dept.setDname("asd1");
		dept.setLoc("zxcvb1");
		DaoHandler dh = new DaoHandler();
		DeptDaoImpl ddao = (DeptDaoImpl) dh.newProxyInstance(new DeptDaoImpl());
		Integer i;
		try {
			i = ddao.insert(dept);
			System.out.println(i);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

测试中,可以在提交SQL的方法中,代码提交后造一个异常,触发回滚,如

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值