关于SpringBoot集成JDBCTemplate的RowMapper


      JdbcTemplate 是Spring提供的一套JDBC模板框架,利用AOP 技术来解决直接使用JDBC时大量重复代码的问题。JdbcTemplate虽然没有MyBatis 那么灵活,但是直接使用JDBC要方便很多。Spring Boot中对Jdbc Template的使用提供了自动化配置类JdbcTemplateAutoConfiguration,部分源码如下:

 1 package org.springframework.boot.autoconfigure.jdbc;
 2 
 3 // 省略 import 行
 4 
 5 @Configuration
 6 // 仅在类 DataSource,JdbcTemplate 存在于 classpath 时生效,
 7 // 这两个类属于 spring-jdbc
 8 @ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
 9 // 仅在单数据源bean存在时才生效
10 @ConditionalOnSingleCandidate(DataSource.class)
11 // 在数据源自动配置应用之后应用
12 @AutoConfigureAfter(DataSourceAutoConfiguration.class)
13 // 确保前缀为 spring.jdbc 的配置参数被加载到 bean JdbcProperties
14 @EnableConfigurationProperties(JdbcProperties.class)
15 public class JdbcTemplateAutoConfiguration {
16 
17     // 内嵌配置类
18     @Configuration
19     static class JdbcTemplateConfiguration {
20 
21         private final DataSource dataSource;
22 
23         private final JdbcProperties properties;
24 
25         JdbcTemplateConfiguration(DataSource dataSource, JdbcProperties properties) {
26             this.dataSource = dataSource;
27             this.properties = properties;
28         }
29 
30        // 定义 bean  JdbcTemplate jdbcTemplate       
31         @Bean
32         @Primary
33         // 仅在此bean没有被定义时才定义
34         @ConditionalOnMissingBean(JdbcOperations.class)
35         public JdbcTemplate jdbcTemplate() {
36             JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSource);
37             JdbcProperties.Template template = this.properties.getTemplate();
38             jdbcTemplate.setFetchSize(template.getFetchSize());
39             jdbcTemplate.setMaxRows(template.getMaxRows());
40             if (template.getQueryTimeout() != null) {
41                 jdbcTemplate
42                         .setQueryTimeout((int) template.getQueryTimeout().getSeconds());
43             }
44             return jdbcTemplate;
45         }
46 
47     }
48 
49     // 内嵌配置类
50     @Configuration
51     // 导入 JdbcTemplateConfiguration 配置类
52     @Import(JdbcTemplateConfiguration.class)
53     static class NamedParameterJdbcTemplateConfiguration {
54 
55         // 定义 bean NamedParameterJdbcTemplate namedParameterJdbcTemplate
56         @Bean
57         @Primary
58         @ConditionalOnSingleCandidate(JdbcTemplate.class)
59         @ConditionalOnMissingBean(NamedParameterJdbcOperations.class)
60         public NamedParameterJdbcTemplate namedParameterJdbcTemplate(
61                 JdbcTemplate jdbcTemplate) {
62             return new NamedParameterJdbcTemplate(jdbcTemplate);
63         }
64 
65     }
66       ......  
67 }    
View Code

  从上面这段源码中可以看出,当classpath下存在DataSource和JdbcTemplate 并且DataSource只有一个实例时,自动配置才会生效,若开发者没有提供JdbcOperations,则Spring Boot会自动向容器中注入一个JdbcTemplate(Jdbc Template是JdbcOperations的子类)。由此可以看到,开发者想要使用Jdbc Template,只需要提供JdbcTemplate的依赖和DataSource依赖即可。具体操作步骤

 

1,创建数据库表并插入数据  

1 CREATE DATABASE 'chapter05'DEFAULT CHARACTER SET utf8;
2 USE‘chapter05;
3 CREATE TABLE book(
4 id int(11)NOT NULL AUTO INCREMENT,
5 name varchar(128)DEFAULT NULL,
6 author varchar(64)DEFAULT NULL,
7 PRIMARY KEY('id')
8 ENGINE=InnoDB DEFAULT CHARSET=utf8;
9 insert into book'(id,name,author)values(1,'三国演义,罗贯中'),(2,'水浒传''施耐庵');
View Code

 

2,添加依赖

 1 <dependency>
 2 <groupId>org.springframework.boot</groupId>
 3 <artifactId>spring-boot-starter-jdbc</artifactId>
 4 </dependency>
 5 <dependency>
 6 <groupId>org.springframework.boot</groupId>
 7 <artifactId>spring-boot-starter-web</artifactId>
 8 </dependency>
 9 <dependency>
10 <groupId>mysql</groupId>
11 <artifactId>mysql-connector-java</artifactId>
12 <scope>runtime</scope>
13 </dependency>
14 <dependency>
15 <groupId>com.alibaba</groupId>
16 <artifactId>druid</artifactId>
17 <version>1.1.9</version>
18 </dependency>
View Code

 

3.数据库配置(注意数据库的配置)
在application.properties中配置数据库基本连接信息:

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///chapter05
spring.datasource.username=root(你自己的)
spring.datasource.password=123(你自己的密码,没有不用填)
View Code

 

4.创建实体类,

创建Book实体类,代码如下:

public class Book{
private Integer id;
private String name;
private String author;
//省略 getter/setter

}
View Code

 

 

5.创建数据库访问层

 1 @Repository
 2 public class BookDao {
 3     @Autowired
 4     JdbcTemplate jdbcTemplate;
 5     public int addBook(Book book) {
 6         return jdbcTemplate.update("INSERT INTO book(name,author) VALUES (?,?)",
 7                 book.getName(), book.getAuthor());
 8     }
 9     public int updateBook(Book book) {
10         return jdbcTemplate.update("UPDATE book SET name=?,author=? WHERE id=?",
11                 book.getName(), book.getAuthor(), book.getId());
12     }
13     public int deleteBookById(Integer id) {
14         return jdbcTemplate.update("DELETE FROM book WHERE id=?", id);
15     }
16     public Book getBookById(Integer id) {
17         return jdbcTemplate.queryForObject("select * from book where id=?",
18                 new BeanPropertyRowMapper<>(Book.class), id);
19     }
20     public List<Book> getAllBooks() {
21         return jdbcTemplate.query("select * from book",
22                 new BeanPropertyRowMapper<>(Book.class));
23     }
24 }
View Code

代码解释:

创建BookDao,注入Jdbc Template。由于已经添加了spring-jdbc相关的依赖,JdbcTemplate会被自动注册到Spring容器中,因此这里可以直接注入Jdbc Template使用。在JdbcTemplate中,增删改三种类型的操作主要使用update和batchUpdate方法来完成。query和queryForObject方法主要用来完成查询功能。另外,还有execute方法可以用来执行任意的SQL、cal方法用来调用存储过程等。·在执行查询操作时,需要有一个/RowMapper f查询出来的列和实体类中的属性一一对应起)来。如果列名和属性名都是相同的,那么可以直接使用BeanPropertyRowMapper;如果列名和属性名不同就需要开发者自己实见RowMapper 接口,将列和实体类属性一一对应起来。

6.创建Service和Controller
创建BookService和BookController,代码如下:

BookService

 1 @Service
 2 public class BookService {
 3     @Autowired
 4     BookDao bookDao;
 5     public int addBook(Book book) {
 6         return bookDao.addBook(book);
 7     }
 8     public int updateBook(Book book) {
 9         return bookDao.updateBook(book);
10     }
11     public int deleteBookById(Integer id) {
12         return bookDao.deleteBookById(id);
13     }
14     public Book getBookById(Integer id) {
15         return bookDao.getBookById(id);
16     }
17     public List<Book> getAllBooks() {
18         return bookDao.getAllBooks();
19     }
20 }
View Code

BookController

 1 package org.sang;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 import java.util.List;
 8 
 9 /**
10  * Created by 半仙
11  */
12 @RestController
13 public class BookController {
14     @Autowired
15     BookService bookService;
16     @GetMapping("/bookOps")
17     public void bookOps() {
18         Book b1 = new Book();
19         b1.setId(99);
20         b1.setName("西厢记");
21         b1.setAuthor("王实甫");
22         int i = bookService.addBook(b1);
23         System.out.println("addBook>>>" + i);
24         Book b2 = new Book();
25         b2.setId(1);
26         b2.setName("朝花夕拾");
27         b2.setAuthor("鲁迅");
28         int updateBook = bookService.updateBook(b2);
29         System.out.println("updateBook>>>"+updateBook);
30         Book b3 = bookService.getBookById(1);
31         System.out.println("getBookById>>>"+b3);
32         int delete = bookService.deleteBookById(2);
33         System.out.println("deleteBookById>>>"+delete);
34         List<Book> allBooks = bookService.getAllBooks();
35         System.out.println("getAllBooks>>>"+allBooks);
36     }
37 }
View Code

最后,在浏览器中访问http://localhost:8080/bookOps地址,控制台打印日志如图5-1所示
addBook>〉>1
updateBoolk>〉>1
getBookById>>>Book{id=1,name='朝花夕拾’,author='鲁迅’}
deleteBoolkById>>>1
getA11Books>>>[Book{id=1,name='朝花夕拾’,author='鲁迅’},Book{id=4,name='西厢记’,author='王实甫’}]

 

  这里只是介绍SpringBoot集成JDBCTemplate的Demo,重点在于上面说的这句话“·在执行查询操作时,需要有一个/RowMapper f查询出来的列和实体类中的属性一一对应起)来。如果列名和属性名都是相同的,那么可以直接使用BeanPropertyRowMapper;如果列名和属性名不同就需要开发者自己实见RowMapper 接口,将列和实体类属性一一对应起来。”

  那么在上面我们看到,JdbcTemplate已经封装了对应增删改的方法,我们只需直接调用即可,关键在于query查询时候RowMapper的这点上,正如上面所说,如果列名和属性名都是相同的,我们可以直接使用BeanPropertyRowMapper,如果列名和属性名不同就需要开发者自己实见RowMapper 接口,将列和实体类属性一一对应起来。”

  那么到底这个RowMapper是什么 ,官方的源码如下

package org.springframework.jdbc.core;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.lang.Nullable;

@FunctionalInterface
public interface RowMapper<T> {
    @Nullable
    T mapRow(ResultSet var1, int var2) throws SQLException;
}

  不难看出这是一个方法接口 ,里面的抽象法中的ResultSet又是什么呢 ,表示数据库结果集的数据表,通常由执行查询数据库的语句生成,这里我只贴出官方的版本,想看的直接点击ctrl+鼠标左键点进去看,我用的是IDEA

  1 **
  2  * A table of data representing a database result set, which
  3  * is usually generated by executing a statement that queries the database.
  4  *
  5  * <P>A <code>ResultSet</code> object  maintains a cursor pointing
  6  * to its current row of data.  Initially the cursor is positioned
  7  * before the first row. The <code>next</code> method moves the
  8  * cursor to the next row, and because it returns <code>false</code>
  9  * when there are no more rows in the <code>ResultSet</code> object,
 10  * it can be used in a <code>while</code> loop to iterate through
 11  * the result set.
 12  * <P>
 13  * A default <code>ResultSet</code> object is not updatable and
 14  * has a cursor that moves forward only.  Thus, you can
 15  * iterate through it only once and only from the first row to the
 16  * last row. It is possible to
 17  * produce <code>ResultSet</code> objects that are scrollable and/or
 18  * updatable.  The following code fragment, in which <code>con</code>
 19  * is a valid <code>Connection</code> object, illustrates how to make
 20  * a result set that is scrollable and insensitive to updates by others, and
 21  * that is updatable. See <code>ResultSet</code> fields for other
 22  * options.
 23  * <PRE>
 24  *
 25  *       Statement stmt = con.createStatement(
 26  *                                      ResultSet.TYPE_SCROLL_INSENSITIVE,
 27  *                                      ResultSet.CONCUR_UPDATABLE);
 28  *       ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
 29  *       // rs will be scrollable, will not show changes made by others,
 30  *       // and will be updatable
 31  *
 32  * </PRE>
 33  * The <code>ResultSet</code> interface provides
 34  * <i>getter</i> methods (<code>getBoolean</code>, <code>getLong</code>, and so on)
 35  * for retrieving column values from the current row.
 36  * Values can be retrieved using either the index number of the
 37  * column or the name of the column.  In general, using the
 38  * column index will be more efficient.  Columns are numbered from 1.
 39  * For maximum portability, result set columns within each row should be
 40  * read in left-to-right order, and each column should be read only once.
 41  *
 42  * <P>For the getter methods, a JDBC driver attempts
 43  * to convert the underlying data to the Java type specified in the
 44  * getter method and returns a suitable Java value.  The JDBC specification
 45  * has a table showing the allowable mappings from SQL types to Java types
 46  * that can be used by the <code>ResultSet</code> getter methods.
 47  *
 48  * <P>Column names used as input to getter methods are case
 49  * insensitive.  When a getter method is called  with
 50  * a column name and several columns have the same name,
 51  * the value of the first matching column will be returned.
 52  * The column name option is
 53  * designed to be used when column names are used in the SQL
 54  * query that generated the result set.
 55  * For columns that are NOT explicitly named in the query, it
 56  * is best to use column numbers. If column names are used, the
 57  * programmer should take care to guarantee that they uniquely refer to
 58  * the intended columns, which can be assured with the SQL <i>AS</i> clause.
 59  * <P>
 60  * A set of updater methods were added to this interface
 61  * in the JDBC 2.0 API (Java&trade; 2 SDK,
 62  * Standard Edition, version 1.2). The comments regarding parameters
 63  * to the getter methods also apply to parameters to the
 64  * updater methods.
 65  *<P>
 66  * The updater methods may be used in two ways:
 67  * <ol>
 68  * <LI>to update a column value in the current row.  In a scrollable
 69  *     <code>ResultSet</code> object, the cursor can be moved backwards
 70  *     and forwards, to an absolute position, or to a position
 71  *     relative to the current row.
 72  *     The following code fragment updates the <code>NAME</code> column
 73  *     in the fifth row of the <code>ResultSet</code> object
 74  *     <code>rs</code> and then uses the method <code>updateRow</code>
 75  *     to update the data source table from which <code>rs</code> was derived.
 76  * <PRE>
 77  *
 78  *       rs.absolute(5); // moves the cursor to the fifth row of rs
 79  *       rs.updateString("NAME", "AINSWORTH"); // updates the
 80  *          // <code>NAME</code> column of row 5 to be <code>AINSWORTH</code>
 81  *       rs.updateRow(); // updates the row in the data source
 82  *
 83  * </PRE>
 84  * <LI>to insert column values into the insert row.  An updatable
 85  *     <code>ResultSet</code> object has a special row associated with
 86  *     it that serves as a staging area for building a row to be inserted.
 87  *     The following code fragment moves the cursor to the insert row, builds
 88  *     a three-column row, and inserts it into <code>rs</code> and into
 89  *     the data source table using the method <code>insertRow</code>.
 90  * <PRE>
 91  *
 92  *       rs.moveToInsertRow(); // moves cursor to the insert row
 93  *       rs.updateString(1, "AINSWORTH"); // updates the
 94  *          // first column of the insert row to be <code>AINSWORTH</code>
 95  *       rs.updateInt(2,35); // updates the second column to be <code>35</code>
 96  *       rs.updateBoolean(3, true); // updates the third column to <code>true</code>
 97  *       rs.insertRow();
 98  *       rs.moveToCurrentRow();
 99  *
100  * </PRE>
101  * </ol>
102  * <P>A <code>ResultSet</code> object is automatically closed when the
103  * <code>Statement</code> object that
104  * generated it is closed, re-executed, or used
105  * to retrieve the next result from a sequence of multiple results.
106  *
107  * <P>The number, types and properties of a <code>ResultSet</code>
108  * object's columns are provided by the <code>ResultSetMetaData</code>
109  * object returned by the <code>ResultSet.getMetaData</code> method.
110  *
111  * @see Statement#executeQuery
112  * @see Statement#getResultSet
113  * @see ResultSetMetaData
114  */
View Code

 

  如何实现自己的RowMapper?参照如下代码

public class BeanRowMapper implements RowMapper<Article> {
  //这里的类名只是为了掩饰   根据自己的情况修改
    @Override
    public Article mapRow(ResultSet resultSet, int i) throws SQLException {
        Bean bean= new Bean();
      //设置不一致的列名与实体字段对应 bean.setId(resultSet.getInt(
"id")); bean.setTitle(resultSet.getString("title")); bean.setDescription(resultSet.getString("description")); return Bean; } }

  然后修改对应的Dao代码

/**
 * 查询所有数据
 */
public List<Bean> findAll() {
    String sql = "SELECT id, title, description FROM Bean";
    return jdbcTemplate.query(sql, new BeanRowMapper());
}

/**
 * 查询单条数据
 */
public BeanfindById(Integer id) {
    String sql = "SELECT id, title, description FROM BeanWHERE id = ?";
    return jdbcTemplate.queryForObject(sql, new BeanRowMapper());
}

  然后在测试对应的接口

才疏学浅,有错误请指出

    

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM