当前位置:   article > 正文

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

java使所有带事务的方法都操作主库

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

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

    

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

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>8.0.23</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-dbcp</groupId>
  8. <artifactId>commons-dbcp</artifactId>
  9. <version>1.4</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>commons-pool</groupId>
  13. <artifactId>commons-pool</artifactId>
  14. <version>1.6</version>
  15. </dependency>

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

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

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

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

  1. package com.my.zonghe.configBuild;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.util.Properties;
  7. public class ConfigBuilder {
  8. static String url;
  9. static String user;
  10. static String password;
  11. static String driverName;
  12. static Integer initialSize;
  13. static Integer maxActive;
  14. static Integer maxWait;
  15. static {
  16. //1、读取配置文件,路径从target/classes下开始
  17. String path = ConfigBuilder.class.getClassLoader().
  18. getResource("com/my/zonghe/config/jdbc.properties").getPath();
  19. //2.使用properties读取内容
  20. Properties ps = new Properties();
  21. InputStream in = null;
  22. try {
  23. in = new FileInputStream(new File(path));
  24. ps.load(in);
  25. url = ps.getProperty("jdbc.pool.url");
  26. user = ps.getProperty("jdbc.pool.user");
  27. password = ps.getProperty("jdbc.pool.password");
  28. driverName = ps.getProperty("jdbc.pool.driverName");
  29. initialSize = Integer.valueOf(ps.getProperty("jdbc.pool.initialSize"));
  30. maxActive = Integer.valueOf(ps.getProperty("jdbc.pool.maxActive"));
  31. maxWait = Integer.valueOf(ps.getProperty("jdbc.pool.maxWait"));
  32. } catch (Exception e) {
  33. // TODO Auto-generated catch block
  34. e.printStackTrace();
  35. } finally {
  36. if(in != null) {
  37. try {
  38. in.close();
  39. } catch (IOException e) {
  40. // TODO Auto-generated catch block
  41. e.printStackTrace();
  42. }
  43. }
  44. }
  45. }
  46. //自己加上get()方法吧
  47. }

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

简单说明:

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

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

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

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

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

  1. package com.my.zonghe.Base;
  2. import org.apache.commons.dbcp.BasicDataSource;
  3. import com.my.zonghe.configBuild.ConfigBuilder;
  4. public class BaseDao {
  5. //1、
  6. private static BasicDataSource basicDataSource;
  7. public static BasicDataSource getBasicDataSource() {
  8. if(basicDataSource == null) {
  9. basicDataSource = new BasicDataSource();
  10. basicDataSource.setUsername(ConfigBuilder.getUser());
  11. basicDataSource.setPassword(ConfigBuilder.getPassword());
  12. basicDataSource.setUrl(ConfigBuilder.getUrl());
  13. basicDataSource.setDriverClassName(ConfigBuilder.getDriverName());
  14. //3、设置连接池使用配置,包括初始化数量、最大连接数、最小空闲数、最大等待时间
  15. basicDataSource.setInitialSize(ConfigBuilder.getInitialSize());
  16. basicDataSource.setMaxActive(ConfigBuilder.getMaxActive());
  17. //从连接池中获取连接的最大等待时间
  18. basicDataSource.setMaxWait(ConfigBuilder.getMaxWait());
  19. }
  20. return basicDataSource;
  21. }
  22. }

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

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

  1. package com.my.zonghe.Base;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.sql.Connection;
  7. import java.sql.SQLException;
  8. import com.my.zonghe.myUtil.Transaction;
  9. public class DaoHandler implements InvocationHandler{
  10. private Object targetObject;
  11. public Object newProxyInstance(Object targetObject) {
  12. this.targetObject = targetObject;
  13. Object newProxyInstance = Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
  14. this.targetObject.getClass().getInterfaces(), this);
  15. //获得动态代理对象
  16. return newProxyInstance;
  17. }
  18. @Override
  19. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  20. Object obj = null;
  21. if(method.getAnnotationsByType(Transaction.class).length > 0) {
  22. //有注解
  23. System.out.println("提交前");
  24. Field field = targetObject.getClass().getSuperclass().getDeclaredField("conn");
  25. field.setAccessible(true);
  26. System.out.println(field.get(targetObject));
  27. Connection conn = (Connection)field.get(targetObject);
  28. conn.setAutoCommit(false);
  29. try {
  30. obj = method.invoke(targetObject, args);
  31. conn.commit();
  32. System.out.println("提交了");
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. conn.rollback();
  36. System.out.println("回滚了");
  37. } finally {
  38. if(conn != null) {
  39. conn.close();
  40. System.out.println("关闭连接了");
  41. }
  42. }
  43. }else {
  44. //不使用事务也得关闭连接
  45. obj = method.invoke(targetObject, args);
  46. Field field = targetObject.getClass().getSuperclass().getDeclaredField("conn");
  47. if(field != null) {
  48. field.setAccessible(true);
  49. Connection conn = (Connection)field.get(targetObject);
  50. if(conn != null) {
  51. conn.close();
  52. System.out.println("关闭连接了");
  53. }
  54. }
  55. }
  56. return obj;
  57. }
  58. }

(4)事务管理注解

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

  1. package com.my.zonghe.Base;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.METHOD)
  8. public @interface Transaction {
  9. public String value() default "";
  10. }

2、测试TestDemo

代码:

  1. package com.my.zonghe.test;
  2. import com.my.zonghe.Base.DaoHandler;
  3. import com.my.zonghe.dept.dao.DeptDao;
  4. import com.my.zonghe.dept.dao.impl.DeptDaoImpl;
  5. import com.my.zonghe.dept.entity.Dept;
  6. public class TestDemo {
  7. public static void main(String[] args) {
  8. Dept dept = new Dept();
  9. dept.setDname("asd1");
  10. dept.setLoc("zxcvb1");
  11. DaoHandler dh = new DaoHandler();
  12. DeptDaoImpl ddao = (DeptDaoImpl) dh.newProxyInstance(new DeptDaoImpl());
  13. Integer i;
  14. try {
  15. i = ddao.insert(dept);
  16. System.out.println(i);
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/576807
推荐阅读
相关标签
  

闽ICP备14008679号