欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring整合JDBC与事务管理

程序员文章站 2022-07-13 21:21:05
...
package com.kkb.test;

import com.kkb.bean.SpringTest;
import com.kkb.dao.StudentDao;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.kkb.service.StudentService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class Test01 {

    private ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
    /*
    Day1025测试:Spring整合JDBC1
    以最基础的方式连接数据库,使用Spring专用的数据库连接的封装类JdbcTemplate连接
    知识点:
    1.创建数据源连接信息,确定连接的Driver类型以及Jdbc连接的地址信息
    2.输入账号密码
    3.用数据源连接信息创建JdbcTemplate
    4.编写要运行的数据库语句,利用JdbcTemplate执行sql语句
    优点:
    1.较最初连接数据库的方法简易许多
    劣势:
    1.由于是初次使用,只是简单的建立了与数据库的连接,没有进行基本的封装
    2.创建对象依旧是用new方法,没有利用Spring框架中创建对象的方式,需要修改
     */
    @Test
    public void test() throws PropertyVetoException {
        //创建数据源连接
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb1?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false");
        dataSource.setUser("root");
        dataSource.setPassword("lovinghurt82");
        //使用JDBCTemplate
        //Spring专门用来封装数据库连接方法的专用类
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        String sql = "INSERT INTO SPRINGTEST (NAME) VALUE (?)";
        int test = jdbcTemplate.update(sql, "test");
        System.out.println("插入的行数:"+test);
    }


    /* Day1025测试:Spring整合JDBC2
    使用Spring自带的Dao父类JdbcDaoSupport来建立数据库之间的连接
    知识点:
    1.通过继承父类JdbcDaoSupport来使用JdbcTemplate
    2.通过Spring容器给JdbcTemplate注入数据源信息DataSource,无需繁琐的手动添加
    3.通过getJdbcTemplate()方法获取父类中的Template来执行SQL语句
    4.update方法的使用
    优势:
    1.无需自己创建JdbcTemplate,直接使用父类提供的template即可,降低了耦合性
    2.通过容器创建对象,符合Spring创建对象的规范
    报错一:NullPointerException空指针异常
    是因为没有给JdbcDaoSupport中的JdbcTemplate赋值,你需要给它赋值它才能知道要连接哪里,
    此处使用容器创建,具体可以查看:application.xml第6-31行
     */
    @Test
    public void testInsert(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        //StudentDao studentDao = new StudentDao();
        SpringTest springTest = new SpringTest();
        springTest.setName("test102501");
        int insert = studentDao.insert(springTest);
        System.out.println("插入的行数:"+insert);
    }


    /*
    Day1025测试:Spring整合JDBC-增删改
    与test02的操作基本相同,只是将insert的方法改成update的方法,并未有太大区别
    知识点:
    1.update方法的使用
     */
    @Test
    public void testUpdate(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        //StudentDao studentDao = new StudentDao();
        SpringTest springTest = new SpringTest();
        springTest.setName("test102502");
        springTest.setId(1);
        int insert = studentDao.update(springTest);
        System.out.println("修改的行数:"+insert);
    }


    /*
    Day1025测试:Spring整合JDBC-增删改
    与test02的操作基本相同,只是将insert的方法改成update的方法,并未有太大区别
    知识点:
    1.Update方法的使用
     */
    @Test
    public void testDelete(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        //StudentDao studentDao = new StudentDao();
        SpringTest springTest = new SpringTest();
        springTest.setId(1);
        int insert = studentDao.delete(springTest);
        System.out.println("删除的行数:"+insert);
    }

    /*
    Day1025测试:Spring整合JDBC-查询1
    根据提供的ID查询数据库中相应的数据
    知识点:
    1.queryForObject方法的使用
    SpringTest springtest = this.getJdbcTemplate().queryForObject(sql,
    //此处传参(数组)↓            ↓此处处理获取的数据以及返回的数据类型
    new Object[]{id}, new RowMapper<SpringTest>() {
                    @Override
                    public SpringTest mapRow(ResultSet resultSet, int i) throws SQLException {
                        SpringTest test = new SpringTest();
                        test.setId(resultSet.getInt("Id"));
                        test.setName(resultSet.getString("Name"));
                        return test;
                    }
                });
    报错2:
    EmptyResultDataAccessException:Incorrect result size: expected 1, actual 0----不正确的结果大小:预期是1,实际是0
    表示没有在数据库中找到相应的数据,应该是查到一个数据,但搜索到的数据与实际不符
     */
    @Test
    public void testFind(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        SpringTest test = new SpringTest();
        try {
            test = studentDao.findById(1);
        }catch (EmptyResultDataAccessException exception){
            exception.printStackTrace();
        }
        System.out.println(test);
    }

    /*
    Day1025测试:Spring整合JDBC-查询2
    知识点:
    1.与findById不同的地方在于,此次用template查询运用的方法是query
    2.在传参RowMap中重写的方法中也只是返回单个对象,并非对象列表,是因为方法中已经封装好了相应的方法,返回的是以单个数据为元素的列表
     */
    @Test
    public void testFindAll(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        List<SpringTest> test = new ArrayList<>();
        try {
            test = studentDao.findAll();
        }catch (EmptyResultDataAccessException exception){
            exception.printStackTrace();
        }
        System.out.println(test);
    }

    /*
    Day1025测试:Spring整合JDBC-查询3
    知识点:
    1.queryForObject的另一个用法,不需要对获得的数据进行操作是,不需要传入参数RowMap
    String sql = "SELECT COUNT(*) FROM SPRINGTEST";
    //                                        此处传入需要获取的数据类型↓
        return this.getJdbcTemplate().queryForObject(sql,Integer.class);
    2.单个数据的获取使用queryForObject,多个数据的获取使用queryForMap
    3.Map中键值对的获取可以使用entrySet来封装,活的Set列表,在通过Map.Entry类中的getKey和getValue方法来获取键值对的值
     */
    @Test
    public void testGetCount(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        int count = studentDao.getCount();
        System.out.println(count);

        Map<String, Object> maxMin = studentDao.getMaxMin();
        Set<Map.Entry<String, Object>> entries = maxMin.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            System.out.println(entry.getKey()+"---------"+entry.getValue());
        }
    }

    /*
    Day1025测试:针对事务管理器接口PlatformTransactionManager处理事务异常情况的实验,基于注解的事务管理
    知识点:
    1.事务管理器是PlatformTransactionManager接口对象,主要用于完成事务的提交、回滚,及获取事务的状态信息
    2.Spring事务的默认回滚方式是:发生运行时异常和error时回滚,发生编译异常时提交。不过对于编译异常,程序员也可以手工设置回滚方式
    3.基于注解的事务需要加上注解@Transactional,注解中要声明事务传播行为常量(propagation)以及回滚方式(rollbackFor),
    事务传播常量常用的有以下几种:
    (1)Propagation.REQUIRED
    当前没有事务的时候,就会创建一个新的事务;如果当前有事务,就直接加入该事务,比较常用的
    (2)设置 Propagation.SUPPORTS
    支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就以非事务方式执行
    (3)Propagation.MANDATORY
    支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就抛出异常
    (4)Propagation.REQUIRES_NEW
    创建新事务,无论当前是否有事务都会创建新的
    4.Spring提供的对事物的管理,就叫做生命是事务管理,在配置文件中配置即可,可以实现对事务控制最大程度的解耦
    5.声明式事务管理的核心实现就是给予AOP
    6.事务的粒度分为粗粒度与细粒度,细粒度即针对方法中的某几行代码进行开启提交回滚,而粗粒度是对整个方法进行开启提交回滚
     */
    @Test
    public void testTransactionManager(){
        StudentService service = (StudentService) ac.getBean("studentService");
        SpringTest test = new SpringTest(12, "test1026");
        int insert = service.insert(test);
        System.out.println(insert);
    }

    /*
    Day1026测试:基于XML的事务管理
    知识点:
    1.Spring事务管理的核心在于AOP,在xml中创建AOP时记得要配置切入点(pointcut)以及事务通知(advisor)
    2.事务通知中需要配置对应方法的名字以及事务传播常量,增删改中常量一般选择REQUIRED,查询的常量一般选择SUPPORT,且需要配置只读选项(read-only)
    3.Pointcut(切入点)指声明的一个或多个连接点的集合。通过切入点指定一组方法。
    注意:被标记为 final 的方法是不能作为连接点与切入点的,因为最终的是不能被修改的,不能被增强的。
    4.Advice(通知/增强)是指拦截到Joinpoint(连接点)之后所要做的事情就是通知。
    通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
    通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
    切入点定义切入的位置,通知定义切入的时间。
     */
    @Test
    public void testTransactionManager02(){
        StudentService service = (StudentService) ac.getBean("studentService");
        SpringTest test = new SpringTest(12, "test1026");
        int insert = service.insert2(test);
        System.out.println(insert);
    }
}