赞
踩
总目标:使用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的方法中,代码提交后造一个异常,触发回滚,如
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。