程序员程序员首页投稿首页投稿(暂停使用,暂停投稿)

SSH框架之旅-spring(3)

2017-10-14  本文已影响56人  Wizey
spring.jpg

1.Spring 的 jdbcTemplate操作


Spring 框架是一站式的框架,针对 JavaEE 的三层结构,每一层都有解决的技术,在 DAO(数据操作层)使用 jdbcTempalte。并且 Spring 也是一个胶水式的框架,对于第三方的框架也有很好的整合支持,对不同的持久层技术都进行封装。

ORM 持久化技术 模板类
JDBC org.springframework.core.JdbcTemplate
Hibernate5.x org.springframework.hibernate5.HibernateTemplate
IBatis(MyBatis) org.springframework.orm.ibatis.sqlMapClientTemplate
JPA org.springframework.orm.jpa.JpaTemplate

下面是 Spring 对数据库的 CURD 操作。

1.1 准备工作

Spring 对 jdbc 做了封装,需要再之前 Spring 的 jar 包的基础上导入 spring-jdbc.jarspring-tx.jar 包,另外记得导入数据库的驱动包。

创建数据库和数据表,Spring 中的 JdbcTemplate 只是对 Jdbc 做了封装,不会为我们自动创建数据表

数据表结构.png

实体类

package cc.wenshixin.jdbc;

public class Student {
    private int id;
    private String name;
    private String sex;
    
    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", sex=" + sex + "]";
    }
}

增删改操作都是调用 update() 方法,Spring 中重载了很多 update() 方法。

1.2 增加操作

    @Test
    //添加操作
    public void add()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "INSERT INTO student(name,sex) VALUES(?,?)";
        int rows = jdbcTemplate.update(sql, "小明", "男");
        System.out.println(rows);
    }

1.3 删除操作

    @Test
    //删除操作
    public void delete()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "DELETE FROM student WHERE id=?";
        int rows = jdbcTemplate.update(sql, 2);
        System.out.println(rows);
    }

1.4 修改操作

    @Test
    //修改操作
    public void update()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "UPDATE student SET name=? WHERE id=?";
        int rows = jdbcTemplate.update(sql, "小张", 2);
        System.out.println(rows);
    }

1.5 查询操作

基础的jdbc查询操作,必会,虽然使用框架不会使用这些基础的代码,但是在无法使用框架时,也可使用基础的方法实现。

    //使用jdbc操作数据库的基本步骤
    @Test
    public void testJdbc() throws SQLException
    {
        Connection conn = null;
        PreparedStatement psmt = null;
        ResultSet rs = null;
        
        try {
            //加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //创建连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false", "root", "root");
            //编写sql语句
            String sql = "SELECT * FROM student WHERE id=?";
            //预编译sql
            psmt = conn.prepareStatement(sql);
            //设置参数
            psmt.setInt(1, 1);
            //执行sql
            rs = psmt.executeQuery();
            //遍历结果集
            while(rs.next())
            {
                //返回得到的结果集
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String sex = rs.getString("sex");
                //放到对象中
                Student student = new Student();
                student.setId(id);
                student.setName(name);
                student.setSex(sex);
                System.out.println(student);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(rs != null)
            {
                rs.close();
            }
            if(psmt != null)
            {
                psmt.close();
            }
            if(conn != null)
            {
                conn.close();
            }
        }
    }

查询数量使用 queryForObject() 方法。

@Test
    //查询数量操作
    public void queryCount()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "SELECT COUNT(id) FROM student";
        int count = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(count);
    }

查询对象时,要先实现实体类的查询实现了 Spring 查询接口 RowMapper 的实现类。

实体类的查询接口实现类

package cc.wenshixin.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class StudentRowMapper implements RowMapper<Student>{

    @Override
    public Student mapRow(ResultSet rs, int num) throws SQLException {
        //从结果集中得到数据
        int id = rs.getInt("id");
        String name = rs.getString("name");
        String sex = rs.getString("sex");
        
        //把得到的结果封装到对象里面
        Student student = new Student();
        student.setId(id);
        student.setName(name);
        student.setSex(sex);
        
        return student;
    }   
}
    @Test
    //查询单个对象操作
    public void queryObject()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "SELECT * FROM student WHERE id=?";
        Student student = jdbcTemplate.queryForObject(sql, new StudentRowMapper(), 1);
        System.out.println(student.toString());
    }

查询多个对象

    @Test
    //查询多个对象操作
    public void queryList()
    {
        //设置数据库信息
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建jdbcTemplate对象,设置数据源
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        
        //调用JdbcTemplate对象里面的方法实现操作
        String sql = "SELECT * FROM student";
        List<Student> studentList = jdbcTemplate.query(sql, new StudentRowMapper());
        System.out.println(studentList);
    }

2.Spring 配置连接池


在做 jdbc 操作时,不是每次都连接数据库,大多是创建一个数据库连接池,每次连接在这个数据库连接池中取连接对象,减少数据库连接和释放的操作。

2.1 准备工作

首先要导入配置 c3p0 连接池的 jar 包,c3p0.jarmchange.jar下载地址,然后创建 Spring 的配置文件。

2.2 配置连接池

以上面的 student 实体类为例,通过 service 类,dao 类,插入一条数据。

配置文件

属性注入数据库配置信息和 jdbcTemplate 的属性。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  <!-- 配置c3p0连接池 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 注入属性值 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property>
    <property name="user" value=""></property>
    <property name="password" value=""></property>
  </bean>
  
  <!-- 配置创建对象 -->
  <bean id="studentService" class="cc.wenshixin.service.StudentService"></bean>
  <bean id="studentDao" class="cc.wenshixin.dao.StudentDao"></bean>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 把dataSource传递到模板对象里面 -->
    <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <!-- 开启注解扫描 -->
  <context:component-scan base-package="cc.wenshixin"></context:component-scan>
</beans>

DAO 类

package cc.wenshixin.dao;

import javax.annotation.Resource;

import org.springframework.jdbc.core.JdbcTemplate;

public class StudentDao {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    
    //插入数据
    public void add()
    {
        String sql = "INSERT INTO student(name,sex) VALUES(?,?)";
        jdbcTemplate.update(sql,"老王","男");
    }
}

Service 类

package cc.wenshixin.service;

import javax.annotation.Resource;

import cc.wenshixin.dao.StudentDao;

public class StudentService {
    @Resource(name="studentDao")
    private StudentDao studentDao;
    
    //添加操作
    public void add()
    {
        studentDao.add();
    }
}

测试类

package cc.wenshixin.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cc.wenshixin.service.StudentService;

public class TestC3p0 {
    @Test
    public void test1()
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        StudentService service = (StudentService) context.getBean("studentService");
        service.add();
    }
}

3.Spring 事务管理


3.1 事务的基本概念

事务是一组逻辑上的操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。

事务的特性:

如果不考虑到隔离性引发的安全性问题:

解决读的问题:设置事务的隔离级别

补充:Mysql 数据库默认的事务隔离级别为可重复读。

3.2 Spring 事务管理的 API

Spring 事务管理有两种方式,一种是编程式事务管理,不使用,另一种式声明式事务管理,基于xml配置文件实现和基于注解实现。

Spring 平台事务管理器的接口 PlatformTransactionManager,针对不同的 DAO 层的框架提供了接口不同的实现类。

事务管理器接口实现类 适用框架
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用 Spring JDBC或IBatis进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager 使用 Hibernate5.x 版本进行持久化数据时使用

下面通过一个银行转账的例子说明 Spring API 的使用

创建数据表,并添加两条实验的数据

初始数据.png

DAO 类

package cc.wenshixin.dao;

import javax.annotation.Resource;

import org.springframework.jdbc.core.JdbcTemplate;

public class DealDao {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    
    public void subMoney(String name, int money)
    {
        String sql = "UPDATE yinhang SET money=money-? WHERE name=?";
        jdbcTemplate.update(sql, money, name);
    }
    
    public void addMoney(String name, int money)
    {
        String sql = "UPDATE yinhang SET money=money+? WHERE name=?";
        jdbcTemplate.update(sql, money, name);
    }
}

Service 类

package cc.wenshixin.service;

import javax.annotation.Resource;

import cc.wenshixin.dao.DealDao;

public class DealService {
    @Resource(name="dealDao")
    private DealDao dealDao;
    
    public void deal()
    {
        //小明转账给小黑1000元
        dealDao.subMoney("小明", 1000);
        dealDao.addMoney("小黑", 1000);
    }
}

xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  <!-- 配置数据库信息 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 注入属性值 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property>
    <property name="user" value=""></property>
    <property name="password" value=""></property>
  </bean>
  
  <!-- 配置对象 -->
  <bean id="dealService" class="cc.wenshixin.service.DealService"></bean>
  <bean id="dealDao" class="cc.wenshixin.dao.DealDao"></bean>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 把dataSource传递到模板对象里面 -->
    <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <!-- 开启注解扫描 -->
  <context:component-scan base-package="cc.wenshixin"></context:component-scan>
</beans>

测试类

package cc.wenshixin.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cc.wenshixin.service.DealService;

public class DealTest {
    @Test
    public void test()
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        DealService dealService = (DealService) context.getBean("dealService");
        dealService.deal();
    }
}

无异常产生,结果为正常

无异常结果.png

下面模拟异常情况的发生,实际情况为银行系统出错了,网络中断等等。

在 Service 类中加入如下代码:

    //小明转账给小黑1000元
    dealDao.subMoney("小明", 1000);
    int i = 10 / 0; //模拟异常
    dealDao.addMoney("小黑", 1000);

再次执行结果,出现除0的异常,但是数据库的结果为

模拟异常结果.png

小明少了1000元,但因为异常,小黑并没有增加1000元,这样肯定是不行的,出现异常需要进行事务回滚。

将配置文件改成下面的配置文件,相比上面的配置文件,加入了事务的约束和事务管理器的配置,另外在要使用事务的方法所在类的上面加上 @Transactional,这个加不加都不影响结果,但是便于代码维护最好加上。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
  <!-- 配置数据库信息 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 注入属性值 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property>
    <property name="user" value=""></property>
    <property name="password" value=""></property>
  </bean>
  
  <!-- 配置对象 -->
  <bean id="dealService" class="cc.wenshixin.service.DealService"></bean>
  <bean id="dealDao" class="cc.wenshixin.dao.DealDao"></bean>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 把dataSource传递到模板对象里面 -->
    <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <!-- 开启注解扫描 -->
  <context:component-scan base-package="cc.wenshixin"></context:component-scan>
    
  <!-- 配置事务管理器 -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    注入dataSource
    <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <!-- 配置事务增强 -->
  <tx:advice id="txadvice" transaction-manager="transactionManager">
    <!-- 做事务操作 -->
    <tx:attributes>
      <!-- 设置进行事务操作的方法匹配规则 -->
      <tx:method name="deal*" propagation="REQUIRED"/>
    </tx:attributes>
  </tx:advice>
  
  <!-- 配置切面 -->
  <aop:config>
    <!-- 切入点 -->
    <aop:pointcut expression="execution(* cc.wenshixin.service.DealService.*(..))" id="pointcut"/>
    <!-- 切面 -->
    <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"/>
  </aop:config>
</beans>

再次执行,出现异常,但是数据库的结果并不会有任何变化。

上一篇下一篇

猜你喜欢

热点阅读