JAVA WEB之Spring4.x JdbcTemplate


jdbcTemplate

說白了,他就是Spring提供用於簡化數據庫訪問的類

基本jdbc驅動訪問數據庫

/* 一個簡易好用的數據庫連接和訪問類 */
package cslg.cn.controller;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class MyDB {

	private static final String database="bookmark";
	private static final String user="root";
	private static final String password="";

	private Connection connection;	
	private Statement statement;

	public void openConnection() throws SQLException{
		try {
			Class.forName("com.mysql.jdbc.Driver");
			String url="jdbc:mysql://localhost:3306/"+database;
			connection=DriverManager.getConnection(url, user, password);
			statement=connection.createStatement();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void closeConnection(){
		try {
			if(statement!=null){
				statement.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	ResultSet executeQuery(String sql) throws SQLException {
		ResultSet rs=statement.executeQuery(sql);
		return rs;
	}

	void executeUpdate(String sql) throws SQLException {
		statement.executeUpdate(sql);
	}
}

基本思路就是:

  1. 連接數據庫時的字符串常量,變量
  2. 加載jdbc類庫
  3. 獲得一個連接
  4. 獲得一個連接的狀態
  5. 通過狀態使用query或者update訪問數據庫

基本可用的也就是讀寫兩個方法:

statement.executeQuery(sql)
statement.executeUpdate(sql)

我們還需要對query方法返回的ResultSet類進行處理:

while (res.next()) {
    BookMark bm = new BookMark(res.getInt(1), res.getString(2), res.getString(3), res.getString(4),res.getInt(5));
    bms.add(bm);
}

如果時多條數據查詢,一般需要一個數據模型類(pojo類),然后建立一個這個模型的List泛型對象,通過ResultSet的next()方法遍歷每一條記錄,使用getInt,getString等方式取得字段。

Spring JdbcTemplate訪問數據庫

如果我們使用像hibernate,mybatits這樣的orm的話,雖然模型映射為sql語句確實非常方便,但是性能上就有一定閹割,在不需要大量數據查詢的小型項目上,我們不用偷懶,寫幾句簡單的sql語句使得查詢和寫入更加有效率。
但是我們像上面那樣每次都通過while語句去取出Result的話,豈不是很麻煩,而且這樣的多個while代碼塊顯得代碼十分冗余。
JdbcTemplate為我們封裝了這些取值操作,減少了寫循環的痛苦,性能上卻並沒有被閹割,和純粹的sql語句查詢相當,因為我們依然要寫出完整的sql語句,而不是采用映射的辦法,所以這個template非常好用的。

項目結構

由於僅僅是學習測試jdbctemplate,所以不需要web訪問,使用Main方法運行,自然建立普通的gradle project即可,並不需要轉換
結構也只需要對java resource添加java類即可:

這里寫圖片描述

配置

配置xml文件,properties文件

如果是web dynamic項目的話xml文件可以web-inf下新建config.xml或者直接在java Resource目錄下新建

config.xml,我使用了4.3版本:

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:c="http://www.springframework.org/schema/c" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	<context:annotation-config />
	<context:component-scan base-package="jdbc"></context:component-scan>
	<context:property-placeholder location="classpath:jdbc.properties" />

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

	<bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"
		p:dataSource-ref="dataSource" />
</beans>

以上對各種Bean做了配置,用於提供JdbcTemplate連接數據庫

jdbc.properties:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/j2ee?characterEncoding=utf8
jdbc.username=root
jdbc.password=

如果是gradle項目:

修改gradle.build以獲得需要的jar包:

apply plugin: 'java-library'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}
ext{
  	springVersion="4.3.1.RELEASE"
}
    

dependencies {
 	compile group: 'org.slf4j', name:'slf4j-api', version:'1.7.21'
    compile group: 'commons-codec', name: 'commons-codec', version: '1.10'
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2.2'
    compile group: 'commons-dbcp', name: 'commons-dbcp', version: '1.4'
    compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.9'
    compile group: 'aopalliance', name: 'aopalliance', version: '1.0'
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.38'
    
    compile(
    	"org.springframework:spring-core:$springVersion",
    	"org.springframework:spring-beans:$springVersion",
    	"org.springframework:spring-aop:$springVersion",
    	"org.springframework:spring-tx:$springVersion",
    	"org.springframework:spring-jdbc:$springVersion",
    	"org.springframework:spring-context:$springVersion",
    	"org.springframework:spring-test:$springVersion"
    )
    // This dependency is exported to consumers, that is to say found on their compile classpath.
    api 'org.apache.commons:commons-math3:3.6.1'

    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:20.0'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}

POJO類

該Project是對數據庫文章進行增刪改查,所以,我新建了一個Article類,為數據庫Article表模型:

package jdbc;

public class Article {

	private int id;
	private String title;
	private String content;
	
	@Override
	public String toString() {
		return "Article [id=" + id + ", title=" + title + ", content=" + content + "]";
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public Article(int id, String title, String content) {
		super();
		this.id = id;
		this.title = title;
		this.content = content;
	}
	public Article(String title, String content) {
		super();
		this.title = title;
		this.content = content;
	}
	public Article(){
		
	}
	
}

Article的DAO層

在面向接口對象編程中

MVC的開發模式:表示層調用控制層,控制層調用業務層,業務層(Service)調用數據訪問層(DAO)
就是這樣層層剝離,解耦,使得一個層的修改不會導致其他層的修改,更利於團隊分層並行編程
當然如果使用的是動態語言,比如php,是不需要剝離出這么多層這么麻煩的!

其中dao是數據訪問層,其實是一個集成了一些專門對某個模型直接訪問數據庫方法的類,比如增刪改查等,便於在業務邏輯層(service層)使用這些方法,操作相應的模型,而hibernate這樣的orm則是通過映射來封裝了相應的模型訪問數據庫方法,做成了DAO層,不必自己去寫

ArticleDAO:

package jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

@Repository
public class ArticleDAO {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	// 查
	public Article find(int id) {
		final String sql = "select * from article where id=?";
		Article ac = jdbcTemplate.queryForObject(sql, new Object[] { id },
				new BeanPropertyRowMapper<Article>(Article.class));
		return ac;
	}

	// 改
	public boolean update(Article a) {
		final String sql = "update article set title=?,content=? where id=?";
		int rc = jdbcTemplate.update(sql, new Object[] { a.getTitle(), a.getContent(), a.getId() });
		return 1 == rc;
	}

	// 刪
	public boolean delete(int id) {
		final String sql = "delete from article where id=?";
		int rc = jdbcTemplate.update(sql, new Object[] { id });
		return 1 == rc;
	}

	// 增
	public int add(Article a) {
		KeyHolder holder = new GeneratedKeyHolder();
		final String sql = "insert into article(title,content) values(?,?)";
		jdbcTemplate.update(new PreparedStatementCreator() {
			@Override
			public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
				PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
				ps.setString(1, a.getTitle());
				ps.setString(2, a.getContent());
				return ps;
			}
		}, holder);
		return holder.getKey().intValue();
	}

	//根據開始和限定條數查詢多條
	public List<Article> list(int begin, int limit) {
		final String sql = "select * from article limit ?,?";
		return jdbcTemplate.query(sql, new Object[] { begin, limit },
				new BeanPropertyRowMapper<Article>(Article.class));
	}

}

注意DAO層提供對數據的訪問方法庫,這里包括了常用的CRUD操作,其中查方法有兩種,一種是單條根據主鍵查詢(id),另一種是指定條數開始和數目的多條查詢(limit),這里需要用到一個數據庫對Bean的映射。

由於jdbcTemplate中的queryForObject方法,第三個參數只能是匹配簡單的Interger,String,Boolean等基本類型,不可以傳入復雜的數據類型,這里直接傳入Article.class是會報異常:
Exception in thread "main" org.springframework.jdbc.IncorrectResultSetColumnCountException: Incorrect column count: expected 1, actual 3

如果是Junit環境下測試是不會獲得此List對象(不匹配,不報異常)

所以使用BeanPropertyRowMapper (T.class),此方法的作用是讓數據庫讀出的數據行的字段和java類下的屬性一一對應(賦值)。queryForObject的最終效果是要讓查詢獲得row自動匹配到相應的java對象!
(這已經很接近orm技術了!)

注:自動綁定,需要列名稱和Java實體類名字一致,如:屬性名 “userName” 可以匹配數據庫中的列字段 "USERNAME" 或 “user_name”。

關於query和queryForObject的區別:
eclipse自動補全中有說明:

query:傳入sql語句,對應的占位對象數組(就是sql中?),RowMapper映射對象。

最終返回的也是RowMapper映射的對象的List列表!

注意是列表!一般用於多條查詢的

這里寫圖片描述

queryForObject:傳入sql,對應的占位對象數組(sql中的?),基本對象的.class屬性,或者RowMapper

如果是基本對象Integer,Boolean等則返回相應的與第三個傳入參數一樣的對象(.class前面的類名)

如果是RowMapper則返回映射后的對象,即第三個參數.class前面的類名!

注:這兒是單個對象,一般用於單條查詢

這里寫圖片描述

MAIN方法

由於我們是相對簡單的應用,不再需要Service層做業務處理,可以直接使用此DAO層進行數據訪問,寫在Main方法中即可:

main:

package jdbc;
import java.util.Scanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		//加載xml配置文件
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:config.xml");
		//取得Bean類並且映射為DAO類
		ArticleDAO aDAO = ctx.getBean(ArticleDAO.class);
		int id = 0;
		Scanner s = new Scanner(System.in);

		while (true) {
			System.out.println("輸入查詢id:");
			id = s.nextInt();
			try {
				Article a = aDAO.find(id); // 傳入id查詢
				System.out.println(a);
			} catch (Exception e) {
				//拋出查不到的異常,不要讓查詢錯誤使得程序結束
				System.out.println("查詢不到結果!");// 傳入id找不到就報錯
			}
			System.out.println();
		}

	}
}

測試結果:

這里寫圖片描述

嘗試其他的DAO方法(list查詢):

這里寫圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM