全栈开发实战 | Spring框架快速入门第三篇

一个人最好的状态:梦想藏在心里,行动落于腿脚。

目录

1、前言

2、Dao模块

2.1 JdbcTemplate

 2.2 事务

2.3 事务特性

2.4 事务控制

2.5 编程式事务控制

2.6 声明式事务控制


1、前言

        上一篇我们已经简单学习了Spring框架的Aop模块,这一篇我们主要介绍Spring框架的DAO模块对JDBC的支持和事务的相关内容

2、Dao模块

原生的JDBC的代码

 public static void main(String[] args) {
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db20", "root", "root");
            //获取传输器
            Statement statement = connection.createStatement();
            //执行Sql语句
            ResultSet resultSet = statement.executeQuery("SELECT * from Student");
            //处理结果集
            while (resultSet.next()){
                String id = resultSet.getString("id");
                String name = resultSet.getString("name");
                String birth = resultSet.getString("birth");
                String sex = resultSet.getString("sex");
                System.out.println(id +":"+name+":"+birth+":"+sex);
            }
            //关闭资源
            resultSet.close();
            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2.1 JdbcTemplate

Spring提供的一种操作数据库的方法,封装了JDBC

使用Spring的注入功能,可以直接将DataSource注入到JdbcTemplate中

接下来我们开始快速入门:

  • 在pom.xml文件中导入所需要的的jar包(之前第一篇文章已经导入过)

  • 创建DataSource,创建JdbcTemplate

 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///yonghedb?serverTimezone=GMT&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
  </bean>
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
   </bean>
  • 编辑EmpDao接口

public interface IEmp {
    void save();
}
  • 编辑EmpDao实现类

@Component
public class EmpDao implements IEmp{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void save() {
        String sql = "INSERT into emp(name,job,salary) VALUES('桔子','程序员',123456789);";
        jdbcTemplate.update(sql);
    }
}
  • 测试代码

public class test1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmpDao empDao = (EmpDao) context.getBean("empDao");
        empDao.save();
    }
}
  • 输出结果:

      结果显示,成功的存入了一条数据

JdbcTemplate查询

  • 我们使用JdbcTemplate查询会发现有很多重载了query()的方法

一般的我们通常会使用queryForMap()这个方法,那么只能封装一行数据,如果封装多行的话,就会报错

又因为返回的值是Map集合,还需要我们自己去转换成自己需要的类型

  • 一般我们用下面这个方法,自定义每行封装成什么类型

  • 编辑EmpDao接口

public interface IEmp {
    void query(int id);
}
  • 编辑EmpDao实现类、


@Component
public class EmpDao implements IEmp{
  @Autowired
  private JdbcTemplate jdbcTemplate;
    @Override
    public void query(int id) {
        String sql = "SELECT * from emp where id = ?";
        RowMapper<Emp> rowMapper = new RowMapper<Emp>(){
            @Override
            public Emp mapRow(ResultSet rs, int i) throws SQLException {
                Emp emp = new Emp();
                emp.setId(rs.getInt("id"));
                emp.setName(rs.getString("name"));
                emp.setJob(rs.getString("job"));
                emp.setSalary(rs.getInt("salary"));
                return emp;
            }
        };
        List<Emp> query = jdbcTemplate.query(sql,rowMapper,id);
        System.out.println(query);
    }
}

  • 测试代码:

public class test1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmpDao empDao = (EmpDao) context.getBean("empDao");
         empDao.query(30);
    }
}
  • 输出结果

 2.2 事务

事务(Transaction)是一个业务,一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性

2.3 事务特性

  • 原子性(Atomicaty): 一个事务中的多个操作要么都成功要么都失败

  • 一致性(Consistency): 一个事务执行之前和执行之后都保持一个状态

  • 隔离性(Isolation): 事务和事务之间是相互隔离的,互不影响

  • 持久性(Durability): 事务一旦提交,数据要永久保存

2.4 事务控制

事务控制分为两种:

  • 编程式事务控制

  • 声明式事物控制

2.5 编程式事务控制

自己手动通过编码方式实现事务,这就叫做编程式事务控制

2.6 声明式事务控制

由Spring框架提供,基于Spring的Aop实现,可以将具体的业务逻辑和事务控制进行解耦,只需要根据步骤进行配置,这就叫做声明式事务控制

从具体配置实现上,Spring提供了两种方式:

  • XML方式实现声明式事物控制

  • 注解方式实现声明式事物控制

第一种:XML方式实现事务控制

  • 搭建配置环境

    上面我们在介绍Spring的Dao模块的时候,JdbcTemplate快速入门的时候,我们的代码是没有事务控制的

    也就是说假如我在EmpService的save方法中调用两次empDao.save时,假如在中间出现异常,那么也会有一条数据保存在数据库中

@Service
public class EmpService {
    @Autowired
    private  EmpDao empDao;
    public void save(){
        //没有事务控制,即使报错了,也会插入一条数据
        empDao.save();
        int i = 1 / 0;
        empDao.save();
    }
}

测试代码:

public class test1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmpService empService = (EmpService) context.getBean("empService");
        empService.save();
    }
}

输出结果:

但是数据库中还是存入了一条数据 

现在我们开始添加事务控制,看看会发生什么?

  • 在配置文件中添加事务的管理器类

 <!--1:配置事物的管理器类-->
 <bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--引用数据库连接池-->
        <property name="dataSource" ref="dataSource"/>
 </bean>
  • 配置事务管理类如何管理事务

<!--2.配置如何管理事务-->
    <tx:advice id="txAdvice" transaction-manager="txManage">
        <!--配置事务的属性-->
        <tx:attributes>
            <!--所有的方法,并不是只读-->
            <tx:method name="*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
  • 配置要拦截哪些方法

<aop:config>
        <aop:pointcut id="pt" expression="execution(* com.cn.service.EmpService.save())"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
  • 再次测试

public class test1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmpService empService = (EmpService) context.getBean("empService");
        empService.save();
    }
}
  • 输出结果

控制台打印出了异常,但是数据库中没有添加数据,事务控制成功 

第二种:注解方式实现事务控制

  • 在配置文件中添加事务的管理器类(和XML的配置方式第一步一样)

 <!--1:配置事物的管理器类-->
    <bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--引用数据库连接池-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
  • 开启注解的方式实现事务

<!--2: 开启以注解的方式实现-->
    <tx:annotation-driven transaction-manager="txManage"></tx:annotation-driven>
  • 最后在需要事务控制的方法上添加@Transactional这个注解就可以(类上添加的话就是控制整个类的事务)

@Service
public class EmpService {
    @Autowired
    private  EmpDao empDao;
    @Transactional//事务管理
    public void save(){
        empDao.save();
        int i = 1 / 0;
        empDao.save();
    }
}
  • 再次测试代码(同上,这里不再添加)

  • 输出结果

控制台依旧打印出了异常,但是数据库中没有添加数据,事务控制成功 。

总结:代码中的@Transactional注解,可以用来描述类和方法,告诉Spring框               架要在哪里进行事务控制,具体说明如下:

  • 当@Transactional注解应用在类上时表示类中的所有方法进行事务控制,并且一般用于事务共性的定义。

  • 当@Transactional注解应用在方法上时,表示只有此方法进行事务控制,假如类和方法上都有这个注解,则方法上的注解一般用于事务特性的定义。

事务属性

第一种我们在配置事务管理器类如何管理事务时,就是在指定事务的属性

第二种我们可以在@Transactional注解后写事务的属性

 下面表格是列出的常见的事务属性:

常见事务属性
timout 事务的超时时间,规定时间内未执行完,事务不提交
read-only 只读事务,默认值为fase,表示只能进行查询,不能进行修改等操作
rollbock-for 指定目标方法出现检查时异常时回滚
no-rollbock-for 指定目标方法出现检查时异常时抛出异常不回滚
isolation 事务的隔离级别,默认值为DEFAULT
propagation

事务的传播行为,常用的是:

1:REQUIRED   表示如果当前方法有事务了,加入当前事务

2:REQUIRED  NEW   表示如果当前方法有事务了,当前方法事务挂起,始终开启一个新的事务,直到新的事务执行完,当前方法的事务才开始

总结:

Spring中事务管理大家以后会用到很多,声明式事务是Spring最核心,最常用的功能。

由于Spring通过IOC和AOP的功能非常透明的实现了声明式事务的功能,对于一般的开发者来说暂时无需了解它的内部细节,能够熟练的进行配置即可

但是如果有时间的话,后期还是可以继续了解深挖一下,毕竟一名程序员的目标还是很明确的。

猜你喜欢

转载自blog.csdn.net/vx1271487114/article/details/125738155
今日推荐