• 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查询):

    这里写图片描述

  • 相关阅读:
    图的邻接链表实现(c)
    图的邻接矩阵实现(c)
    好文
    第13章 红黑树
    函数 setjmp, longjmp, sigsetjmp, siglongjmp
    域名解析
    wget www.baidu.com执行流程分析
    信号处理篇
    第11章 散列表
    第10章,基本数据结构(栈,队列)
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/6844769.html
Copyright © 2020-2023  润新知