Spring框架 - 数据访问 Spring JDBC

#数据访问

  • 数据库访问,JDBC
  • Spring事务管理
  • ORM整合

##DAO

  • Data Access Object
    数据访问对象
  • 数据访问相关接口
    在使用过程中,会有DAO接口,针对DAO接口具有不同的实现。但是业务对象,使用DAO的对象通过接口的形式来使用DAO对象。在DAO层可以有不同种类的实现,比如JDBC实现,也可以整合其他的ORM框架,例如MyBatis、Hibernate实现。以接口的方式进行实现,可以屏蔽掉底层的细节,让业务对象根据接口使用DAO。

输入图片说明

##ORM

  • Object Relation Mapping
  • 对象关系映射
    数据库记录到Java对象的映射关系,自动的把数据库记录转化为对应的Java代码,也可以把Java代码转化成对应的数据库记录。

输入图片说明

#Why Spring JDBC? 输入图片说明

##数据访问 使用JDBC的方式去访问数据的过程的步骤

  • 连接参数
    连接什么地址,用户名密码是什么
  • 打开连接
    创建到目标数据库的连接
  • 声明SQL语句以及参数
  • 执行SQL,并循环访问结果
  • 执行业务
    针对每行的访问结果执行特定的业务
  • 处理异常
  • 关闭连接,语句以及结果集
    关闭资源

这些步骤里面,业务里面真正需要关心的是连接参数:必须去关心,没有连接参数就没办法进行连接;声明SQL语句与参数:直接和业务需求相关,例如查询某种用户的结果集,或者符合某种条件的结果集,不声明SQL无法执行结果;执行业务:循环访问结果对于用户来说,如果有某种方式来告诉每行记录都是什么样子的,只要根据结果集做执行对应的业务就可以。
从用户的角度来看,只需要做这三样事情,就可以去完成业务需求,对于其他的底层实现,如何打开连接、如何访问结果、如何处理异常关闭资源,从用户角度来说并没有对业务产生很大的影响。Spring JDBC的作用就是封装这些用户不关心的底层细节内容。只要关心的内容,通过接口暴露出来,只需要关心编写对应的业务。Spring JDBC提高编程效率,关系业务细节。

##DataSource Spring里面是使用DataSource类接口进行代表的。DataSource代表一个数据源,如下内容就是数据源所需要的参数。

  • 驱动类名
  • 连接地址
  • 用户名称
  • 用户密码

通过这几个参数可以构成一个DataSource,根据特定的需求定义成不同的数据源,比如可以定义成MySQL数据源,也可以定义成Oracle的数据源。

DataSource方法唯一的基础方法就是 getConnection,比如我们建立一个DataSource的连接,我们获取连接以后就可以做数据查询相关的事情,例如查询SQL,创建表

##DataSource DataSource是接口,并不是由Spring提供的,而是由Java本身的JavaEE提供的接口,而Spring只是在这个基础之上提供了一个实现DriverManagerDatasource,基础JDBC提供的DataSource。但是这个实现有一个问题,就是每次使用时都会创建一次连接。在一些场景下,例如频繁查询都会创建一次连接,从性能上来讲并不是很好,所以就会有类似线程池一样的数据库连接池的概念,但是这个Spring本身并没有提供,而是由Apache Commons DBCP提供的BasicDataSource的实现。BasicDataSource是在org.apache.commons.dbcp包下实现类。BasicDataSource实现了数据库连接池的功能,这样就不需要每次查询的时候,都去创建一个连接,实现连接的复用

输入图片说明

##BasicDataSource配置 BasicDataSource配置模板

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

       <bean id="dataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">
              <property name="driverClassName" value="${jdbc.driver}" />
              <property name="url" value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
              <property name="testOnBorrow" value="false" />
              <property name="testWhileIdle" value="true" />
              <!-- 连接池启动时的初始值 -->
              <property name="initialSize" value="10" />
              <!-- 连接池的最大值 -->
              <property name="maxActive" value="100" />
              <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
              <property name="maxIdle" value="50" />
              <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
              <property name="minIdle" value="10" />
              <!--#给出一条简单的sql语句进行验证-->
              <property name="validationQuery" value="select getdate()" />
              <!--#在取出连接时进行有效验证-->
              <property name="removeAbandonedTimeout" value="120" />
              <property name="removeAbandoned" value="true" />
              <!-- #运行判断连接超时任务的时间间隔,单位为毫秒,默认为-1,即不执行任务。 -->
              <property name="timeBetweenEvictionRunsMillis" value="3600000" />
              <!-- #连接的超时时间,默认为半小时。 -->
              <property name="minEvictableIdleTimeMillis" value="3600000" />
       </bean>

</beans>

通过db.properties获取数据库连接

<context:property-placeholder location="db.properties" />

上面这种方式是比较简单的加载方式,效果等同于下面的方式

<bean id = "headerProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigure">
    <property name="location" value="classpath:db.properties" />
</bean>

##JdbcTemplate 声明SQL及参数、执行SQL及处理结果、异常处理都封装在叫做JdbcTemplate的类里面,所在的包为org.springframework.jdbc.core

##JdbcTemplate与JDBC差别 例如,下面的代码查询了SQL有多少行,这里通过queryForObject方法执行SQL语句,并把结果集转换成Integer.class。

String sql = "SELECT COUNT(*) FROM user";
int rowCount = this.jdbcTemplate.queryForObject(sql, Integer.class);

也可以查询某批用户的数量,这里可以传入SQL的参数

String sql = "SELECT COUNT(*) FROM user WHERE frist_name = ?";
int countOfNamedJoe = this.jdbcTemplate.queryForObject(sql,Integer.class,"Joe");

我们还可以插入其他类型,比如查找某个用户id的lastName是什么,大家可以看到,我们把参数放到了Object[]当中。

String sql = "SELECT COUNT(*) FROM user WHERE id = ?";
String lastName = this.jdbcTemplate.queryForObject(sql,new Object[]{1212L},String.class);

**注意:**上面两个例子的参数放置是相反的, JdbcTemplate提供了很多查询相关插入相关的接口,一般都会具有两种不同类型的接口,比如返回参数类型放到前面查询参数放到后面,另外一种刚好相反。 主要原因是因为Java函数获取不确定参数个数的时候,是在最后的参数添加...

##JdbcTemplate增改删方法 ###新增,插入

String sql = "INSERT INTO user (first_name,last_name) values (? ,?)";
this.jdbcTemplate.update(sql,"Meimei","Han");

###更新

String sql = "UPDATE user SET last_name=? WHERE id = ?";
this.jdbcTemplate.update(sql, "Li",5276L);

###删除

String sql = "DELETE FROM user WHERE id = ?";
this.jdbcTemplate.update(sql, Long.valueOf(userId));

###创建表格

String sql = "create table user (id integer,first_name varchar(100),last_name varchar(100))";
this.jdbcTemplate.execute(sql);

##JdbcTemplate 转换对象 ORM是把数据库的信息转换成对象,JdbcTemplate也是可以完成该功能的。
JdbcTemplate提供了接口叫做RowMapper,可以通过RowMapper把ResultSet一一的转换成你所需要的对象,例如下面的例子,只有一行数据,id为1212的用户,返回了User的Java对象。

String sql = "SELECT first_name,last_name FROM user WHERE id = ?";
User user = this.jdbcTemplate.queryForObject(sql,new Object[]{1212L},new RowMapper<User>(){
    public User mapRow(ResultSet rs,int rowNum) throws SQLException{
        User user = new User();
        user.setFirstName(rs.getString("first_name"));
        user.setLastName(rs.getString("last_name"));
        }
});

也可以像如下方法返回List<User>

String sql = "SELECT first_name,last_name FROM user";
List<User> users = this.jdbcTemplate.queryForObject(sql,new RowMapper<User>(){
    public User mapRow(ResultSet rs,int rowNum) throws SQLException{
        User user = new User();
        user.setFirstName(rs.getString("first_name"));
        user.setLastName(rs.getString("last_name"));
        }
});

###总结JdbcTemplate使用起来比较简单,没有打开连接,转换结果集,关闭连接,释放资源等操作。

##定义JdbcTemplate ###Java代码形式定义JDBCTemplate 在设置DataSource的时候创建JdbcTemplate

public class JdbcExampleDao implements ExampleDao {
    
        private JdbcTemplate jdbcTemplate;
        
        public void setDataSource(DataSource dataSource){
                this.jdbcTemplate = new JdbcTemplate(dataSource);
            }

        // ... DAO 接口实现
}

###XML声明使用JdbcTemplate

<bean id="exampleDao" class="com.netease.course.JdbcExampleDao" >
    <property name= "dataSource" ref="dataSource" />
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
              <property name="driverClassName" value="${jdbc.driver}" />
              <property name="url" value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
</bean>

<context:property-placeholder location="jdbc.properties" />

###通过Annotation(注解)方式配置JdbcTemplate @Repository这个代表是DAO的Bean,是对数据进行了访问,@Autowired来进行自动的注入

@Repository
public class JdbcExampleDao implements ExampleDao {
    
        private JdbcTemplate jdbcTemplate;
        
        @Autowired
        public void setDataSource(DataSource dataSource)    {
                this.jdbcTemplate = new JdbcTemplate(dataSource);
                }

        // ... DAO 接口实现
}

##代码演示JdbcTemplate的使用 maven的基本依赖,jdbcTemplate的基本依赖

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.3.3.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
		  <groupId>mysql</groupId>
		  <artifactId>mysql-connector-java</artifactId>
		  <version>5.1.39</version>
		</dependency>

添加数据库配置文件db.properties

jdbc.driverClassName= com.mysql.jdbc.Driver
jdbc.url= jdbc:mysql://192.168.1.200:3306/example
jdbc.username=root
jdbc.password=

在Spring配置文件配置数据源

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

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<context:property-placeholder location="db.properties" />

测试用的DAO

package com.hava.spring_data.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;

/**
 * Created by yanfa on 2016/10/24.
 */
@Repository
public class MyJdbcTemplateDao {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource){
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void createTable()
    {
        System.out.println("ceateTable");
        String sql = "CREATE TABLE user (id INTEGER,first_name VARCHAR(100), last_name VARCHAR(100))";

        jdbcTemplate.execute(sql);
    }
}

主执行类

package com.hava.spring_data;

import com.hava.spring_data.repository.MyJdbcTemplateDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by yanfa on 2016/10/24.
 */
public class SpringJdbcMain {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");

        MyJdbcTemplateDao jdbcTemplateDao = context.getBean("myJdbcTemplateDao", MyJdbcTemplateDao.class);
        jdbcTemplateDao.createTable();

        ((ConfigurableApplicationContext) context).close();
    }
}

执行时发生异常如下:

java.sql.SQLException: Access denied for user 'root'@'192.168.1.100' (using password: NO)

由于密码错误,更正密码则运行正确 输入图片说明

jdbcTemplate插入数据

    public void insertData(){
        this.jdbcTemplate.update("INSERT INTO user VALUES (1, ?, ?)", "Meimie","Han");
        this.jdbcTemplate.update("INSERT INTO user VALUES (1, ?, ?)", "Lei","Li");
    }

jdbcTemplate获取行数

    public int count(){
        String sql = "SELECT COUNT(*) FROM user";
        return this.jdbcTemplate.queryForObject(sql,Integer.class);
    }

执行结果

ceateTable
2

##SQL结果转换成Java对象 Entity对象类

package com.hava.spring_data.entity;

/**
 * Created by yanfa on 2016/10/24.
 */
public class User {

    private int id;

    private String first_name;

    private String last_name;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirst_name() {
        return first_name;
    }

    public void setFirst_name(String first_name) {
        this.first_name = first_name;
    }

    public String getLast_name() {
        return last_name;
    }

    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }
}

jdbcTemplate查询

public List<User> getUserList(){
        return this.jdbcTemplate.query("SELECT * FROM user", new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
                User user = new User();
                user.setId(resultSet.getInt("id"));
                user.setFirst_name(resultSet.getString("first_name"));
                user.setLast_name(resultSet.getString("last_name"));
                return user;
            }
        });
    }

结果正确

##NamedParameterJdbcTemplate 在插入数据,可以通过?的形式插入参数。但是有很多列,每列都是?无法确定意义。由此,Spring提供了NamedParameterJdbcTemplate,不在通过?形式定义参数,而是通过命名的形式

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
    @Autowired
    public void setDataSource(DataSource dataSource){
        //this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    }


    public int countOfUsersByFirstName(String firstName){
        String sql = "SELECT COUNT(*) FROM user WHERE first_name = :firstName;";

        Map<String ,String> nameParameters = Collections.singletonMap("firstName",firstName);

        return this.namedParameterJdbcTemplate.queryForObject(sql,nameParameters,Integer.class);
    }

运行成功

##NamedParameterJdbcTemplate的接口形式

  • querForObject(String sql,Map<String,?> paramMap,RowMapper<T> rowMapper);
  • querForObject(String sql,SqlParameterSource paramSource,RowMapper<T> rowMapper);
    还有SqlParameterSrouce的接口,通改接口可以实现实现自己的实现逻辑

##SqlParameterSource 在Spring就有简单的实现,最简单是MapSqlParameterSource,使用上和之前是类似的。还提供了另外一种比较方便的形式BeanPropertySqlParameterSource,如果是对象的话,我们使用map就比较麻烦,而使用BeanPropertySqlParameterSource就简单的多。这些Spring对SqlParameterSource的实现都是在org.springframework.jdbc.core.namedparam包里面。

输入图片说明

##BeanPropertySqlParameterSource的使用 使用BeanPropertySqlParameterSource,则需要有Entity对象类 在使用时,我们需要直接传入对象,而不需要传入Map。

    public int countOfUsersByFirstName(User user){
        String sql = "SELECT COUNT(*) FROM user WHERE first_name = :first_name;";

        SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(user);

        return this.namedParameterJdbcTemplate.queryForObject(sql,sqlParameterSource,Integer.class);
    }

**注意:**这里的User的属性,必须和所填入注入的SQL参数的名称保持一致。这里的SQL查询和上一个的查询SQL并不一致。

##异常处理 SQL的异常时checked的异常,要么捕获异常,要么在接口抛出异常。但是无法连接、语法错误、表列不存在,都会影响应用的正常使用,不是正常状态。

输入图片说明

Spring是把SQLException转换到了DataAccessException,DataAccessException是unchecked的异常。写代码时不会try-catch。不用再代码中散布异常捕获。在业务处理的时候,在最终要返回业务的节点做处理。如果不能回复则给用户进行返回

输入图片说明

##DataAccessException 在Spring中DataAccessException是基类,有很多异常处理都是基于DataAccessException。我们可以根据子类的不同名称判断错误类型。

输入图片说明

转载于:https://my.oschina.net/u/3666693/blog/2208500

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值