Spring框架 - 数据访问 Spring JDBC

#数据访问java

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

##DAOmysql

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

输入图片说明

##ORMspring

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

输入图片说明

#Why Spring JDBC? 输入图片说明sql

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

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

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

##DataSource Spring里面是使用DataSource类接口进行表明的。DataSource表明一个数据源,以下内容就是数据源所须要的参数。编程

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

经过这几个参数能够构成一个DataSource,根据特定的需求定义成不一样的数据源,好比能够定义成MySQL数据源,也能够定义成Oracle的数据源。app

DataSource方法惟一的基础方法就是 getConnection,好比咱们创建一个DataSource的链接,咱们获取链接之后就能够作数据查询相关的事情,例如查询SQL,建立表框架

##DataSource DataSource是接口,并非由Spring提供的,而是由Java自己的JavaEE提供的接口,而Spring只是在这个基础之上提供了一个实现DriverManagerDatasource,基础JDBC提供的DataSource。可是这个实现有一个问题,就是每次使用时都会建立一次链接。在一些场景下,例如频繁查询都会建立一次链接,从性能上来说并非很好,因此就会有相似线程池同样的数据库链接池的概念,可是这个Spring自己并无提供,而是由Apache Commons DBCP提供的BasicDataSource的实现。BasicDataSource是在org.apache.commons.dbcp包下实现类。BasicDataSource实现了数据库链接池的功能,这样就不须要每次查询的时候,都去建立一个链接,实现链接的复用maven

输入图片说明

##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。咱们能够根据子类的不一样名称判断错误类型。

输入图片说明

相关文章
相关标签/搜索