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。咱們能夠根據子類的不一樣名稱判斷錯誤類型。

輸入圖片說明

相關文章
相關標籤/搜索