天天看點

Spring 架構JDBC

為了使 JDBC 更加易于使用, Spring 在 JDBC API 上定義了一個抽象層, 以此建立一個 JDBC 存取架構。

作為 Spring JDBC 架構的核心, JDBC 模闆的設計目的是為不同類型的 JDBC 操作提供模闆方法. 每個模闆方法都能控制整個過程, 并允許覆寫過程中的特定任務. 通過這種方式, 可以在盡可能保留靈活性的情況下, 将資料庫存取的工作量降到最低。

Spring 架構提供了 JdbcTemplate 來操作資料庫。JdbcTemplate 類被設計成為線程安全的, 是以可以再 IOC 容器中聲明它的單個執行個體, 并将這個執行個體注入到所有的 DAO 執行個體中。

Spring JDBC 架構還提供了一個 JdbcDaoSupport 類來簡化 DAO 實作. 該類聲明了 jdbcTemplate 屬性, 它可以從 IOC 容器中注入, 或者自動從資料源中建立。

在經典的 JDBC 用法中, SQL 參數是用占位符 ? 表示,并且受到位置的限制。 定位參數的問題在于, 一旦參數的順序發生變化, 就必須改變參數綁定.。

在 Spring JDBC 架構中, 綁定 SQL 參數的另一種選擇是使用具名參數(named parameter).

具名參數: SQL 按名稱(以冒号開頭)而不是按位置進行指定. 具名參數更易于維護, 也提升了可讀性. 具名參數由架構類在運作時用占位符取代

具名參數隻在 NamedParameterJdbcTemplate 中得到支援。

下面我們舉例說明

例子

spring-jdbc.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: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/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置掃描包 -->
    <context:component-scan base-package="com.spring.jdbc.test"></context:component-scan>
    <!-- 導入資源檔案 -->
    <context:property-placeholder location="classpath:db.properties"/>
    <!-- 配置C3P0資料源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>

        <!-- <property name="initPoolSize" value="${jdbc.initPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> -->
    </bean>

    <!-- 配置jdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置 NamedParameterJdbcTemplate, 該對象可以使用具名參數, 其沒有無參數的構造器, 是以必須為其構造器指定參數 -->
    <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="dataSource"></constructor-arg>
    </bean>
</beans>
           

db.properties

jdbc.user=root
jdbc.password=
jdbc.jdbcUrl=jdbc:mysql:///test
jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.initPoolSize=
jdbc.maxPoolSize=
           

JDBCTest.java

package com.spring.jdbc.test;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

public class JDBCTest {

    private ApplicationContext ctx;
    private JdbcTemplate jdbcTemplate;
    private EmployeeDao employeeDao;
    private DepartmentDao departmentDao;
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    {
        ctx = new ClassPathXmlApplicationContext("spring-jdbc.xml");
        jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        employeeDao = (EmployeeDao) ctx.getBean("employeeDao");
        departmentDao = (DepartmentDao) ctx.getBean("departmentDao");
        namedParameterJdbcTemplate = (NamedParameterJdbcTemplate) ctx.getBean("namedParameterJdbcTemplate");
    }

    /**
     * 使用具名參數時, 可以使用 update(String sql, SqlParameterSource paramSource) 方法進行更新操作
     * 1. SQL 語句中的參數名和類的屬性一緻!
     * 2. 使用 SqlParameterSource 的 BeanPropertySqlParameterSource 實作類作為參數. 
     */
    @Test
    public void testNamedParameterJdbcTemplate2() {
        String sql = "INSERT INTO employee(id, last_name, email, dep_id) VALUES(:id, :lastName, :email, :depId)";
        Employee employee = new Employee();
        employee.setDepId();
        employee.setId();
        employee.setEmail("[email protected]");
        employee.setLastName("GG");

        SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
        namedParameterJdbcTemplate.update(sql, paramSource);
    }

    /**
     * 可以為參數起名字. 
     * 1. 好處: 若有多個參數, 則不用再去對應位置, 直接對應參數名, 便于維護
     * 2. 缺點: 較為麻煩. 
     */
    @Test
    public void testNamedParameterJdbcTemplate() {
        String sql = "INSERT INTO employee(id, last_name, email, dep_id) VALUES(:id, :lastName,:email,:depid)";

        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("id", );
        paramMap.put("lastName", "FF");
        paramMap.put("email", "[email protected]");
        paramMap.put("depid", );

        namedParameterJdbcTemplate.update(sql, paramMap);
    }

    @Test
    public void testDepartmentDao() {
        System.out.println(departmentDao.get());
    }


    @Test
    public void testEmployeeDao() {
        System.out.println(employeeDao.get());
    }

    /**
     * 擷取單個列的值, 或做統計查詢
     * 使用 queryForObject(String sql, Class<Long> requiredType) 
     */
    @Test
    public void testQueryForObject2() {
        String sql = "SELECT count(id) FROM employee";
        long count = jdbcTemplate.queryForObject(sql, Long.class);
        System.out.println(count);
    }

    /**
     * 查到實體類的集合
     * 注意調用的不是 queryForList 方法
     */
    @Test
    public void testQueryForList() {
        String sql = "SELECT id, last_name as lastName, email, dep_id as depId FROM employee WHERE id > ?";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
        List<Employee> employees = jdbcTemplate.query(sql, rowMapper, );
        System.out.println(employees);
    } 

    /**
     * 從資料庫中擷取一條記錄, 實際得到對應的一個對象
     * 注意不是調用 queryForObject(String sql, Class<Employee> requiredType, Object... args) 方法!
     * 而需要調用 queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)
     * 1. 其中的 RowMapper 指定如何去映射結果集的行, 常用的實作類為 BeanPropertyRowMapper
     * 2. 使用 SQL 中列的别名完成列名和類的屬性名的映射. 例如 last_name lastName
     * 3. 不支援級聯屬性. JdbcTemplate 到底是一個 JDBC 的小工具, 而不是 ORM 架構
     */
    @Test
    public void testQueryForObject() {
        String sql = "SELECT id, last_name as lastName, email, dep_id as depId FROM employee WHERE id = ?";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
        Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, );
        System.out.println(employee);
    }

    /**
     * 執行批量更新: 批量的 INSERT, UPDATE, DELETE
     * 最後一個參數是 Object[] 的 List 類型: 因為修改一條記錄需要一個 Object 的數組, 那麼多條不就需要多個 Object 的數組嗎
     */
    @Test
    public void testBatchUpdate() {
        String sql = "INSERT INTO employee(id, last_name, email, dep_id) values(?, ?, ?, ?)";
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        batchArgs.add(new Object[]{, "AA", "[email protected]", });
        batchArgs.add(new Object[]{, "BB", "[email protected]", });
        batchArgs.add(new Object[]{, "CC", "[email protected]", });
        batchArgs.add(new Object[]{, "DD", "[email protected]", });
        jdbcTemplate.batchUpdate(sql, batchArgs);
    }

    /**
     * 執行 INSERT, UPDATE, DELETE
     */
    @Test
    public void testUpdate() {
        String sql = "INSERT INTO department(id, name) VALUES(?, ?)";
        jdbcTemplate.update(sql, , "市場部");
    }

    @Test
    public void testDataSource() throws SQLException {
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource.getConnection());
    }
}
           

DepartmentDao.java

package com.spring.jdbc.test;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

/**
 * 不推薦使用 JdbcDaoSupport, 而推薦直接使用 JdbcTempate 作為 Dao 類的成員變量
 */
@Repository
public class DepartmentDao extends JdbcDaoSupport {

    @Autowired
    public void setDataSource2(DataSource dataSource){
        setDataSource(dataSource);
    }

    public Department get(Integer id) {
        String sql = "SELECT id, name FROM department WHERE id = ?";
        RowMapper<Department> rowMapper = new BeanPropertyRowMapper<Department>(Department.class);
        return getJdbcTemplate().queryForObject(sql, rowMapper, id);
    }
}
           

EmployeeDao.java

package com.spring.jdbc.test;

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

@Repository
public class EmployeeDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public Employee get(Integer id) {
        String sql = "SELECT id, last_name as lastName, email, dep_id as depId FROM employee WHERE id = ?";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
        return jdbcTemplate.queryForObject(sql, rowMapper, id);
    }
}
           

Department.java

package com.spring.jdbc.test;

public class Department {

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

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

Employee.java

package com.spring.jdbc.test;

public class Employee {

    private Integer id;
    private String lastName;
    private String email;
    private Integer depId;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getDepId() {
        return depId;
    }
    public void setDepId(Integer depId) {
        this.depId = depId;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", lastName=" + lastName + ", email="
                + email + ", depId=" + depId + "]";
    }
}
           

繼續閱讀