Spring中dbUtil的概念和搭建使用

目录

1、什么是dbUtil

2、lombok插件

3、dbUtil通过xml来实现

4、SpringJunit的作用和效果

(1)标记测试方法和配置方法的注解

5、dbUtil通过注释来实现

6、dbUtil通过config配置类来实现

7、dbUtil通过aop的XML的实现

(1)连接工具类

(2)事务工具类

(3)service层业务事务逻辑实现    


1、什么是dbUtil

        dbUtil阿帕奇提供操作数据库的插件,它主要用于减少样板代码,并简化Java程序与数据库的交互。

  • 简化ResultSet处理:DBUtils 提供了 ResultSetHandler 接口和一些常用的实现类,用于将 ResultSet 转换为Java对象或集合。
  • 简化SQL执行:DBUtils 提供了 QueryRunner 类,用于执行SQL查询和更新操作。它提供了简洁的方法来处理参数和处理结果集。
  • 处理资源的关闭:DBUtils 提供了 DbUtils 类,其中包含关闭数据库连接、语句和结果集的方法,以确保资源在使用后被正确释放。

2、lombok插件

        如果你有一个人员信息的bean类,如果其中删除某个数属性或者添加某个属性,难道我们要去重新写所有的set、get、构造方法吗,这时候就可以用idea提供的lombok插件来完成这个代码维护。

        lombok是用于简化 Java 编码过程中的样板代码(boilerplate code)。它通过注解处理器在编译时生成代码,帮助开发者减少编写样板代码的负担。Lombok 提供了多种注解,用于自动生成常见的 Java 代码,例如 getter、setter、构造函数、toString 方法等。

        首先在pom.xml配置lombok配置:

       <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>

        接下来就可以在pojo类中去简化一个Bean对象的getset等诸多方法。

// 这个注解会生成一个无参构造方法。
@NoArgsConstructor
// 这个注解会生成一个包含所有字段的构造方法。
@AllArgsConstructor
// 这个注解是一个综合性注解,它会生成以下常用的方法:
// getter和setter方法
// toString方法
// equals和hashCode方法
// 还会生成一个canEqual方法
@Data
public class Account {
    private int aid;
    private String aname;
    private int amoney;

    public Account(String aname, int amoney) {
        this.aname = aname;
        this.amoney = amoney;
    }

}
  • @NoArgsConstructor:是生成的无参构造方法。
  • @AllArgsConstructor:是自动生成包含所有字段的构造函数。
  • @RequiredArgsConstructor:自动生成包含 final 字段和带有 @NonNull 注解字段的构造函数。
  • @Data:自动生成多个常见的样板代码,包括 getter、setter、toString、equals 和 hashCode 方法等。

3、dbUtil通过xml来实现

        首先是XML中定义一个数据库连接池Bean注入数据源

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${msg1}"></property>
        <property name="jdbcUrl" value="${msg2}"></property>
        <property name="user" value="${msg3}"></property>
        <property name="password" value="${msg4}"></property>
    </bean>

        id属性指的是Bean名称,class这里指的是ComboPooledDataSource,是C3P0提供的一个实现类,这个实现类中有driverClass等jdbc的set方法,通过使用Spring的占位符(${}),这些属性值从外部配置文件中读取。

        dataSorce是管理数据库连接的,使用 getConnection() 方法可以从数据源中获取一个 Connection 对象,从而执行 SQL 查询和更新操作。

       

        然后是注入QueryRunner

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

        这里的class是org.apache.commons.dbutils.QueryRunner类创建的一个queryRunner的实例,再通过ds这个构造函数注入数据源dataSource。dataSource 是之前定义的 ComboPooledDataSource的Bean,用于管理数据库连接。

        然后是注入dao、注入service、注入controller

    <!--注入dao-->
    <bean id="mapperImp" class="com.apesource.dao.AccountMapperImp">
        <property name="queryRunner" ref="queryRunner"></property>
    </bean>

    <!--注入service-->
    <bean id="service" class="com.apesource.service.AccountServiceImp">
        <property name="mapper" ref="mapperImp"></property>
    </bean>

    <!--注入controller-->
    <bean id="controller" class="com.apesource.controller.AccountControllerImp">
        <property name="service" ref="service"></property>
    </bean>

        其中dao注入的是queryRunner,用它来执行SQL查询和更新操作,与数据库之间的交互。

        其次是dao层中的接口和实现类:

        接口IAccountMapper:

public interface IAccountMapper {
    public void save(Account account);
    public Account findByName(String name);
    public List<Account> findAll();
    public void updateById(Account account);
    public void deleteById(int id);
}

        里面定义了增删改查的抽象方法。 

        其次是AccountMapperImp实现类:

public class AccountMapperImp implements IAccountMapper {

    //操作数据库的核心类
    QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    @Override
    public void save(Account account) {
        try {
            queryRunner.update("insert into account(aname,amoney) value(?,?)",account.getAname(),account.getAmoney());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    @Override
    public Account findByName(String name) {
        try {
            queryRunner.query("select * from account where name = ?",new BeanHandler<Account>(Account.class),name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Account> findAll() {
        try {
            return queryRunner.query("select * from account",new BeanListHandler<Account>(Account.class));
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    @Override
    public void updateById(Account account) {
        try {
            queryRunner.update("update account set aname=?,amoney=? where aid=?",account.getAname(),account.getAmoney(),account.getAid());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    @Override
    public void deleteById(int id) {
        try {
            queryRunner.update("delete from account where aid=?",id);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

        首先实现类继承了接口,重新了其中的方法,其中在中间创建一个成员变量queryRunner用于执行数据库操作的核心类,并通过set方法通过xml注入的方式设置 QueryRunner 实例。

        QueryRunner中有两个方法,一个为query,用来查询。一个为update,用来增删改。?为要传的参数,后面依次跟上传入的参数值。

        其中findAll()方法中是要查询所有的学生,返回的学生集合,所以这里出现了new BeanListHandler<Account>,因为 BeanListHandler 是 Apache Commons DBUtils 库中用于处理 ResultSet 的一种处理器。它将结果集中的每一行映射为一个 Java 对象,并将这些对象收集到一个 List 中

        

        service层、controller层:用户传入参数给controller层,controller层传给service层,一层套一层来传递,这里我就只给一个service层的代码,controller层一样的道理。

public class AccountServiceImp implements IAccountService {

    private IAccountMapper mapper;

    public void setMapper(IAccountMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public void save(Account account) {
        mapper.save(account);
    }

    @Override
    public Account findByName(String name) {
        return mapper.findByName(name);
    }

    @Override
    public List<Account> findAll() {
        return mapper.findAll();
    }

    @Override
    public void updateById(Account account) {
        mapper.updateById(account);
    }

    @Override
    public void deleteById(int id) {
        mapper.deleteById(id);
    }
}

        通过controller层传递来的参数,通过XML里面的Bean来创建一个mapper对象,再把参数传递给mapper的方法,去dao层中的方法实现逻辑。

4、SpringJunit的作用和效果

        然后就是如果我们的想要去调用方法难道要写很多个测试类吗,所以Spring中提供了Junit,用于编写和运行可重复的测试。它主要用于单元测试,但也可以用于集成测试。JUnit 提供了一组注解、断言和运行测试的工具,使得测试代码变得简洁而易于理解。

(1)标记测试方法和配置方法的注解
@Test可以运行的测试方法
@Before在每个@Test运行之前执行。常用于初始化测试环境。
@After在每个@Test运行之后执行。常用于清理测试环境。
@BeforeClass在所有@Test运行之前执行一次。常用于耗时的全局初始化。
@AfterClass在所有@Test运行之后执行一次。常用于全局资源的释放。

        实例代码(手动管理):

public class Test01 {
    ClassPathXmlApplicationContext applicationContext = null;
    IAccountController controller = null;

    @Before
    public void beforeMethod() {
        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        controller = (IAccountController) applicationContext.getBean("controller");
    }

    @After
    public void afterMethod() {
        applicationContext.close();
    }

    @Test
    public void show1() {
        controller.save(new Account("大头", 2000));
        controller.save(new Account("小头", 2000));
    }

    @Test
    public void show2() {
        List<Account> all = controller.findAll();
        for (int i = 0; i < all.size(); i++) {
            Account account = all.get(i);
            System.out.println(account);
        }
    }

    @Test
    public void show3(){
        controller.updateById(new Account(2,"大头",1000));
    }
}

        上面的代码是去手动的调用applicationContext对象和getBean方法,有没有一种写法可以不用手动的去写这些需求操作呢。这里就用到自动管理

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test02 {
    @Autowired
    IAccountController controller;


    @Test
    public void show1(){
        controller.save(new Account("林航宇",2000));
        controller.save(new Account("杨文琪",2000));
    }

    @Test
    public void show2(){
        List<Account> all = controller.findAll();
        for (int i = 0; i < all.size(); i++) {
            Account account =  all.get(i);
            System.out.println(account);
        }
    }
}

        @RunWith()这个注解告诉 JUnit 使用SpringJUnit4ClassRunner类来运行测试。这是一个 Spring 提供的 Runner,它可以在 JUnit 测试中加载 Spring 应用上下文,并将 Spring 的依赖注入机制与 JUnit 测试结合起来。

        @ContextConfiguration()这个注解指定了 Spring 应用上下文的配置文件位置。从类路径加载 applicationContext.xml 配置文件。Spring 会根据这个配置文件创建应用上下文,并进行 Bean 的初始化和依赖注入。

5、dbUtil通过注释来实现

        其实和通过xml差不多,只不过将bean标签注入换成了扫描注入通过在controller、service、dao层加上注释来完成bean对象的注入。

        将原本的构造注入换成了注释来实现标签注入

@Controller("controller")
public class AccountControllerImp implements IAccountController{

    @Autowired
    private IAccountService service;

        再在XML文件中把bean标签注入删除,换成扫描:

<context:component-scan base-package="com.apesource"></context:component-scan>

        这里就不过多描述了,跟XML注入非常接近。

6、dbUtil通过config配置类来实现

        其实也跟前两种非常的接近,就是多了一层config配置类,通过加载配置类来连接数据库和创建bean实例。

@Configuration
@PropertySource(value = "classpath:jdbc.properties")
public class DataConfig {
    @Value("${msg1}")
    private String driverClass;
    @Value("${msg2}")
    private String jdbcUrl;
    @Value("${msg3}")
    private String user;
    @Value("${msg4}")
    private String password;

    @Bean
    public DataSource dataSource(){
        try {
            ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
            comboPooledDataSource.setDriverClass(driverClass);
            comboPooledDataSource.setJdbcUrl(jdbcUrl);
            comboPooledDataSource.setUser(user);
            comboPooledDataSource.setPassword(password);
            return comboPooledDataSource;
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    @Bean
    public QueryRunner queryRunner(){
        return new QueryRunner(dataSource());
    }

}

        @Configuration:标记 DataConfig 类为 Spring 的配置类,表示这个类包含一个或多个 @Bean 方法,用于定义 Spring 容器中的 Bean。

        @PropertySource("classpath:jdbc.properties"):指定一个或多个属性文件,用于加载应用程序的属性配置。在这里,jdbc.properties 文件位于类路径下。Spring 会将这些属性文件中的属性加载到 Environment 中,以便通过 @Value 注解进行注入。

        @Value是将配置文件中的属性值注入到字段中,将从 jdbc.properties 文件中读取并注入到 DataConfig 类中。

        然后又定义了DataSource,通过 ComboPooledDataSource 配置数据库连接池,创建了dataSource对象。QueryRunner:使用注入的 DataSource 实例来创建 QueryRunner Bean,用于简化数据库操作。

        再通过DataConfig配置类的实现来合作完成ApplicationConfig:

@Configuration
@ComponentScan(basePackages = "com.apesource")
@Import(DataConfig.class)
public class ApplicationConfig {
}

        这里@ComponentScan是扫描组件扫描的基础包路径,Spring 会扫描 com.apesource 包及其子包中的所有类,自动识别并注册带有 @Component、@Service、@Repository、@Controller 等注解的 Bean。

applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        在测试类中通过AnnotationConfigApplicationContext来获取context。

7、dbUtil通过aop的XML的实现

        如果我们在service层写一个逻辑,一个人对另一个人借了多少钱,呢双方的金额都要进行改变,但是如果中间发生了异常,但是没有事务的注入没有发生回滚,那么就会发生逻辑错误,一个人钱少了,一个人没拿到钱。

        这时候我们就要进行注入连接工具类和事务工具类了。

    <!--连接工具类-->
    <bean id="connectionUtil" class="com.apesource.util.ConnectionUtil">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--事务工具类-->
    <bean id="transactionUtil" class="com.apesource.util.TransactionUtil">
        <property name="connectionUtil" ref="connectionUtil"></property>
    </bean>

        在XML中加入这两个注入,一个为管理数据库连接的工具类。在其中注入用构造注入完成了对dataSource的注入。另一个为处理处理数据库事务的工具类,在其中设置 connectionUtil 属性。

        如果想要同时提交或回滚那么就要用同一个connection同一个业务方法的多个dao方法公用一个connection对象,为了确保每个线程都有一个数据库连接,我们就要创建一个Connect再创建一个ConnectionUtil类来管理数据库的连接。如果有连接则就用当前连接,如果没有连接则创建连接。

(1)连接工具类
public class ConnectionUtil {
    //装配数据源
    DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    //线程区域对象
    ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();


    //获取连接
    public Connection createCon(){
        try {
            //1.获取线程内的连接对象
            Connection connection = threadLocal.get();
            //2.判断
            if (connection==null){
                connection = dataSource.getConnection();//创建连接
                threadLocal.set(connection);//保存
            }
            return connection;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    //移除连接
    public void removeCon(){
        threadLocal.remove();//移除连接对象
    }

}

        ThreadLocal<Connection>:用于在每个线程中存储一个 Connection 实例。

        createCon方法:用于获取当前线程的数据库连接。其中的threadLocal.get():尝试从 ThreadLocal 中获取当前线程的 Connection 实例。如果 ThreadLocal 中没有连接(即 connection 为 null),则通过 dataSource.getConnection() 创建一个新的连接,并将其保存到 ThreadLocal 中。如果有则直接返回当前的connection对象。

        removeCon 方法:用于从 ThreadLocal 中移除当前线程的 Connection 实例和连接。

        

(2)事务工具类
public class TransactionUtil {
    //注入连接工具类
    ConnectionUtil connectionUtil;

    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

    //开启事务
    public void beginTx(){
        try {
            connectionUtil.createCon().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //提交事务
    public void commitTx(){
        try {
            connectionUtil.createCon().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //回滚事务
    public void rollbackTx(){
        try {
            connectionUtil.createCon().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //关闭事务
    public void closeTx(){
        try {
            connectionUtil.createCon().close();//关闭事务
            connectionUtil.removeCon();//移除事务
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

        这个类用于管理数据库事务。它封装了事务的开启、提交、回滚和关闭操作,并依赖于 ConnectionUtil 来获取数据库连接,因为里面定义了dataSourse。

        然后就是事务的开启、提交、回滚、关闭等方法操作。

        开启事务:通过调用createCon()方法获取当前线程的连接,并将其自动提交模式设置为 false。这意味着事务需要手动提交或回滚。

        提交/回滚事务:获取当前线程的连接提交/回滚事务。

        关闭事务:connectionUtil.createCon().close():关闭当前线程的连接,将其返回到连接池中。connectionUtil.createCon().close():关闭当前线程的连接,将其返回到连接池中。

(3)service层业务事务逻辑实现    

        呢么在哪个层加入这个转账的方法呢,就是在service的业务逻辑层来完成逻辑的实现。

//装配
    TransactionUtil transactionUtil;

    public void setTransactionUtil(TransactionUtil transactionUtil) {
        this.transactionUtil = transactionUtil;
    }
@Override
    public void transfer(String sourceName, String targetName, int money) {

        try {
            transactionUtil.beginTx();

            //1.查询数据
            Account sourceAccount = mapper.findByName(sourceName);
            Account targetAccount = mapper.findByName(targetName);
            //2.转账
            sourceAccount.setAmoney(sourceAccount.getAmoney()-money);
            targetAccount.setAmoney(targetAccount.getAmoney()+money);
            //3.修改数据库
            mapper.updateById(sourceAccount);
            // int a = 10/0;//模拟异常
            mapper.updateById(targetAccount);

            transactionUtil.commitTx();
        } catch (Exception e) {
            e.printStackTrace();
            transactionUtil.rollbackTx();
        }finally {
            transactionUtil.closeTx();
        }
    }

        进行装配,在XML中id为service的bean自动构造注入实例化TransactionUtil事务工具类对象。

        首先调用 TransactionUtil 的 beginTx 方法开始一个事务。这会将连接的自动提交模式设置为 false,意味着所有对数据库的操作都将在一个事务中执行,直到提交或回滚。

        其次就是对转账这个逻辑进行相应的操作,查看数据、转账、修改数据库。

        紧接着调用.commitTx方法提交事务。service逻辑层就完成了事务的逻辑操作了。

        

        在dao层中的实体类完成了注入连接工具类:

    ConnectionUtil connectionUtil;

    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

        呢为什么要在dao层获取连接工具类呢,第一,在TransactionUtil中定义了一个ConnectionUtil的成员变量,所以在dao层实例化一个ConnectionUtil对象就可以被service层中的TransactionUtil对象调用。第二,TransactionUtil 需要通过 ConnectionUtil 来获取数据库连接。第三,dao层是与dataSource有直接关系的,所以用dao层来实例化ConnectionUtil。第四,ConnectionUtil要用dataSource,TransactionUtil要用ConnectionUtil,所以逐一迭代的关系分别放在service层和dao层。

        而controller层只用调用transfer方法即可,将参数传入service层。

        总而言之,这种实现方法的本质为通过连接对象对事务的统一管理

  • 32
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值