#數據訪問java
##DAOmysql
##ORMspring
#Why Spring JDBC? sql
##數據訪問 使用JDBC的方式去訪問數據的過程的步驟數據庫
這些步驟裏面,業務裏面真正須要關心的是鏈接參數:必須去關心,沒有鏈接參數就沒辦法進行鏈接;聲明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);
##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。咱們能夠根據子類的不一樣名稱判斷錯誤類型。