前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SSH框架之旅-spring(3)

SSH框架之旅-spring(3)

作者头像
Wizey
发布2018-08-30 09:41:46
4280
发布2018-08-30 09:41:46
举报
文章被收录于专栏:编程心路

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

实体类

代码语言:javascript
复制
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 增加操作

代码语言:javascript
复制
    @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 删除操作

代码语言:javascript
复制
    @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 修改操作

代码语言:javascript
复制
    @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查询操作,必会,虽然使用框架不会使用这些基础的代码,但是在无法使用框架时,也可使用基础的方法实现。

代码语言:javascript
复制
    //使用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() 方法。

代码语言:javascript
复制
@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 的实现类。

实体类的查询接口实现类

代码语言:javascript
复制
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;
    }   
}
  • 查询单个对象
代码语言:javascript
复制
    @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());
    }

查询多个对象

代码语言:javascript
复制
    @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 的属性。

代码语言:javascript
复制
<?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 类

代码语言:javascript
复制
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 类

代码语言:javascript
复制
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();
    }
}

测试类

代码语言:javascript
复制
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 事务的基本概念

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

事务的特性:

  • 原子性:强调事务是不可分割
  • 一致性:事务的执行的前后数据的完整性保持一致
  • 隔离性:一个事务执行的过程中,不应该收到其他事务的干扰
  • 持久性:事务一旦结束,数据就持久到数据库

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

  • 脏读:一个事务读到了另一个事务的未提交的数据
  • 不可重复读:一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致
  • 虚读:一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致

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

  • 未提交读:脏读,不可重复读,虚读都有可能发生
  • 已提交读:避免脏读,但是不可重复读和虚读有可能发生
  • 可重复读:避免脏读和不可重复读,但是虚读有可能发生
  • 串行化:避免以上所有读的问题

补充: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 类

代码语言:javascript
复制
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 类

代码语言:javascript
复制
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 配置文件

代码语言:javascript
复制
<?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>

测试类

代码语言:javascript
复制
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 类中加入如下代码:

代码语言:javascript
复制
    //小明转账给小黑1000元
    dealDao.subMoney("小明", 1000);
    int i = 10 / 0; //模拟异常
    dealDao.addMoney("小黑", 1000);

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

模拟异常结果.png

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

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

代码语言:javascript
复制
<?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>

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.10.14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Spring 的 jdbcTemplate操作
    • 1.1 准备工作
      • 1.2 增加操作
        • 1.3 删除操作
          • 1.4 修改操作
            • 1.5 查询操作
            • 2.Spring 配置连接池
              • 2.1 准备工作
                • 2.2 配置连接池
                • 3.Spring 事务管理
                  • 3.1 事务的基本概念
                    • 3.2 Spring 事务管理的 API
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档