NamedParameterJdbcTemplate使用详解

Posted CUIYD_1989

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NamedParameterJdbcTemplate使用详解相关的知识,希望对你有一定的参考价值。

一、什么是NamedParameterJdbcTemplate

NamedParameterJdbcTemplate类拓展了JdbcTemplate类,对JdbcTemplate类进行了封装从而支持**具名参数特性**。

什么是具名参数?
SQL 按名称(以冒号开头)⽽不是按位置进⾏指定。

例如:

  • 传统的jdbcTemplate的SQL语句定义,通过问号进行占位,如下:
 private final String QUERY_SQL = "INSERT INTO T_USER(username,password) VALUES(?,?)";
  • NamedParameterJdbcTemplate的吗具名参数定义SQL语句:
private final String QUERY_SQL = "INSERT INTO T_USER(username,password) VALUES(:username,:password)";

NamedParameterJdbcTemplate主要提供以下三类方法:execute方法、query及queryForXXX方法、update及batchUpdate方法。

二、准备工作

1、创建工程

2、添加Maven依赖

<!--		jdbc依赖-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

<!--		连接数据库依赖-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

<!--		lombok依赖-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

另外:jdbc依赖也可以换成

<!--		jdbcTemplate 依赖-->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.0.9.RELEASE</version>
		</dependency>

3、yaml配置

#MySql8.0
spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/数据库名?useSSL=false&characterEncoding=utf-8&useUnicode=true&serverTimezone=Asia/Shanghai
    username: 数据库用户名
    password: 数据库密码

4、数据库结构

注意:数据库的id主键设置成自动递增,所以添加的时候不用考虑id

5、JavaBean实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TUser implements Serializable 

    private Integer id;
    private String username;
    private String password;


三、实际使用

1.支持的类

(1)SqlParameterSource

可以使用SqlParameterSource实现具名参数,默认实现有 :

  1. MapSqlParameterSource,只是封装了java.util.Map
  2. BeanPropertySqlParameterSource封装了一个JavaBean对象,通过JavaBean对象属性设置具名参数值
  3. EmptySqlParameterSource 一个空的SqlParameterSource ,占位使用

(2)RowMapper

这个接口为了实现sql查询结果和对象间的转换,可以自己实现,也可以使用系统实现,主要实现类有:

  1. SingleColumnRowMapper,sql结果为一个单列的数据,如List , List,String,Integer等
  2. BeanPropertyRowMapper,sql结果匹配到对象 List< XxxVO> , XxxVO

2、插入/修改/删除数据,使用update方法

(1)Map作为参数

API: int update(String sql, Map<String, ?> paramMap)

 	@Autowired
    private NamedParameterJdbcTemplate template;
    
    //添加数据
		Map<String,Object> map = new HashMap<>();
        map.put("username","刘亦菲");
        map.put("password","4983ghh");
        template.update("INSERT INTO T_USER(username,password) VALUES(:username,:password)",map);
        
 //修改数据
Map<String,Object> map = new HashMap<>();
map.put("username","刘诗诗");
map.put("password","ewgg");
map.put("id",4);
template.update("UPDATE T_USER SET USERNAME = :username,PASSWORD = :password WHERE ID = :id",map);

//        删除数据
Map<String,Object> map = new HashMap<>();
map.put("id",4);
template.update("DELETE FROM T_USER WHERE ID = :id",map);

(2)BeanPropertySqlParameterSource作为参数

API: int update(String sql, SqlParameterSource paramSource)

  1. 创建参数DTO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConditionDTO 

    private String password;

    private String username;


  1. 给DTO属性传入数据并作为参数传入
ConditionDTO conditionDTO = new ConditionDTO();
conditionDTO.setUsername("成龙");
conditionDTO.setPassword("2432tgh");

BeanPropertySqlParameterSource beanParam = new BeanPropertySqlParameterSource(conditionDTO);
template.update("INSERT INTO T_USER(username,password) VALUES(:username,:password)",beanParam);

注意:用BeanPropertySqlParameterSource作为参数的时候,可以将参数DTO属性存值单独出来一个方法,这样可以解耦,代码维护相对轻松。如下:

private ConditionDTO getConditionDTO() 
        ConditionDTO conditionDTO = new ConditionDTO();
        conditionDTO.setUsername("成龙");
        conditionDTO.setPassword("2432tgh");
        return conditionDTO;
    

BeanPropertySqlParameterSource beanParam = new BeanPropertySqlParameterSource(getConditionDTO());
template.update("INSERT INTO T_USER(username,password) VALUES(:username,:password)",beanParam);

(3)MapSqlParameterSource 作为参数

API: int update(String sql, SqlParameterSource paramSource)

MapSqlParameterSource mapSql = new MapSqlParameterSource();
mapSql.addValue("username","李连杰")
      .addValue("password","huewrgowrei");
template.update("INSERT INTO T_USER(username,password) VALUES(:username,:password)",mapSql);

3、查询

(1)返回单行单列数据

注意:单行单列不是单独的一行或者单独的一列数据,单列单行是只有一个数据,如下:

API: public < T > T queryForObject(String sql, Map<String, ?> paramMap, Class<T> requiredType)

API: public < T > T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)

Integer count = template.queryForObject(
                "SELECT COUNT(*) FROM T_USER", new HashMap<>(), Integer.class);

使用EmptySqlParameterSource

String username = template.queryForObject( "SELECT USERNAME FROM T_USER WHERE ID = 4", EmptySqlParameterSource.INSTANCE, String.class); 

(2)返回 (多行)单列 数据

多行单列形式:

API: public < T> List< T> queryForList(String sql, Map<String, ?> paramMap, Class< T > elementType)

API: public < T> List< T> queryForList(String sql, SqlParameterSource paramSource, Class< T> elementType)

List< String> nameList = template.queryForList("SELECT USERNAME FROM T_USER", new HashMap<>(), String.class);
nameList.forEach(System.out::println);

控制台打印结果:

(3)返回单行数据

解释:数据库中的一行数据对应的就是java中的一个bean实体

API:public < T> T queryForObject(String sql, Map< String, ?> paramMap, RowMapper< T>rowMapper)

API: public < T> T queryForObject(String sql, SqlParameterSource paramSource, RowMapper< T> rowMapper)

//BeanPropertyRowMapper会把下划线转化为驼峰属性
TUser user = template.queryForObject(
                "SELECT * FROM T_USER LIMIT 1", new HashMap<>(), new BeanPropertyRowMapper<TUser>(TUser.class));
System.out.println("查询出来的user是:"+user);

(4)返回Map形式的单行数据

API: public Map< String, Object> queryForMap(String sql, Map< String, ?> paramMap)

API: public Map< String, Object> queryForMap(String sql, SqlParameterSource paramSource)

 @RequestMapping("/querySingleTwo")
    public Map testQueryForObjectTwo() 
        Map< String, Object> userMap = template.queryForMap("SELECT * FROM T_USER LIMIT 1", new HashMap<>());
        System.out.println(userMap);
        return userMap;
    

postman返回结果:

(5)返回多行数据

API: public < T> List< T> query(String sql, Map< String, ?> paramMap, RowMapper< T> rowMapper)
API: public < T> List< T> query(String sql, SqlParameterSource paramSource, RowMapper< T> rowMapper)
API: public < T> List< T> query(String sql, RowMapper< T> rowMapper)

	@RequestMapping("/querySingleTwo")
    public List< TUser> testQueryForObjectTwo() 
        List< TUser> userList = template.query(
                "SELECT * FROM T_USER",
                new BeanPropertyRowMapper<>(TUser.class)
        );
        return userList;
    

postman返回结果:

[
    
        "id": 1,
        "username": "admin",
        "password": "admin"
    ,
    
        "id": 2,
        "username": "abc",
        "password": "123"
    ,
    
        "id": 3,
        "username": "abcd",
        "password": "1234"
    ,
    
        "id": 4,
        "username": "abcde",
        "password": "12345"
    ,
    
        "id": 109,
        "username": "刘亦菲",
        "password": "4983ghh"
    ,
    
        "id": 110,
        "username": "刘亦菲",
        "password": "4983ghh"
    ,
    
        "id": 111,
        "username": "成龙",
        "password": "2432tgh"
    ,
    
        "id": 112,
        "username": "李连杰",
        "password": "huewrgowrei"
    
]

(6)返回多行数据(Map)

API: public List< Map< String, Object>> queryForList(String sql, Map< String, ?> paramMap)
API: public List< Map< String, Object>> queryForList(String sql, SqlParameterSource paramSource)

	@RequestMapping("/querySingleTwo")
    public  List<Map<String, Object>> testQueryForObjectTwo() 
        List<Map<String, Object>> mapList = template.queryForList(
                "SELECT * FROM T_USER", new HashMap<>());
        return mapList;
    

postman返回结果:

[
    
        "id": 1,
        "username": "admin",
        "password": "admin"
    ,
    
        "id": 2,
        "username": "abc",
        "password": "123"
    ,
    
        "id": 3,
        "username": "abcd",
        "password": "1234"
    ,
    
        "id": 4,
        "username": "abcde",
        "password": "12345"
    ,
    
        "id": 109,
        "username": "刘亦菲",
        "password": "4983ghh"
    ,
    
        "id": 110,
        "username": "刘亦菲",
        "password": "4983ghh"
    ,
    
        "id": 111,
        "username": "成龙",
        "password": "2432tgh"
    ,
    
        "id": 112,
        "username": "李连杰",
        "password": "huewrgowrei"
    
]

4、获取新增的主键

NamedParameterJdbcTemplate还新增了KeyHolder类,使⽤它我们可以获得主键,类似Mybatis中的useGeneratedKeys。

代码示例:

 @RequestMapping("/querySingleTwo")
    public int testQueryForObjectTwo() 
        String sql = "INSERT INTO T_USER(username,password) VALUES(:username,:password)";
        ConditionDTO conditionDTO = new ConditionDTO("r43g", "呼呼");
        SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(conditionDTO);
        KeyHolder keyHolder = new GeneratedKeyHolder();
        template.update(sql, sqlParameterSource, keyHolder);
        int k = keyHolder.getKey().intValue();
        System.out.println("主键值是:"+k);
        return k;
    

返回结果就是新增的主键。

5、批量操作 batchUpdate

方法源码:

(1)批量添加

SqlParameterSource[] 参数为例

		List<TUser> list = new ArrayList<>();
        TUser tUser1 = new TUser();
        tUser1.setPassword("t7493857vd");
        tUser1.setUsername("给会儿");

        TUser tUser2 = new TUser();
        tUser2.setPassword("erwghrthey");
        tUser2.setUsername("国瑞");

        list.add(tUser1);
        list.add(tUser2);

        SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(list.toArray());

        template.batchUpdate("INSERT INTO T_USER(username,password) VALUES(:username,:password)",batch);

(2)批量更新

		List<TUser> list = new ArrayList<>();
        TUser tUser1 = new TUser();
        tUser1.setPassword("eryg");
        tUser1.setUsername("个人谈话人");
        tUser1.setId(109);

        TUser tUser2 = new TUser();
        tUser2.setPassword("我二哥");
        tUser2.setUsername("个羊肉汤");
        tUser1.setId(110);

        TUser tUser3 = new TUser();
        tUser3.setPassword("gerhr6");
        tUser3.setUsername("反倒是规范");
        tUser3.setId(111);

        list.add(tUser1);
        list.add(tUser2);
        list.add(tUser3);

        SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(list.toArray());

        template.batchUpdate("UPDATE T_USER SET USERNAME = :username,PASSWORD = :password WHERE ID = :id",batch);

(3)批量删除

		List<TUser> list = new ArrayList<>();
        TUser tUser1 = new TUser();
        tUser1.setId(109);

        TUser tUser2 = new TUser();
        tUser2.setId(112);

        TUser tUser3 = new TUser();
        tUser3.setId(113);

        list.add(tUser1);
        list.add(tUser2);
        list.add(tUser3)这里有一篇文章举例说明区别:medium.com/@7anac/…
                
              
                
            
        
    

            

 
             
            【参考方案1】: 
                

    
    

没有可衡量的差异表现。 NamedParameterJdbcTemplate 是一种方便,允许您使用命名参数。 如果您真的很好奇,请查看可随时下载的源代码。 我发现阅读源代码让我对我在论坛上得到的答案更有信心。

【讨论】:

【参考方案2】:

当你使用 JdbcTemplate 时,你给它的 SQL 有一个? 占位符,代表你想要替换到 SQL 中的每个参数。当您在代码中分配参数时,您必须在数组中传递参数,并且它们会按照它们在数组中出现的顺序被使用,如下所示:

Object[] args = new Object[] "x", "y";
String sql = "select * from foo where a = ? and b = ?";
jdbcTemplate.query(sql, args, resultSetExtractor);

所以运行的 SQL 是 select * from foo where a = 'x' and b = 'y'

NamedParameterJdbcTemplate 允许您为参数占位符分配名称并传入映射,以便模板可以将映射名称与占位符匹配。所以你的代码看起来像:

String sql = "select * from foo where a = :mya and b = :myb";
Map<String, Object> argMap = new HashMap<String, Object>();
argMap.put("mya", "x");
argMap.put("myb", "y");
namedParameterJdbcTemplate.query(sql, argMap, resultSetExtractor);

生成与第一个示例相同的 SQL。

运行查询是耗时的部分,参数插入的性能不是问题。

这个想法是通过名称匹配参数比必须以特定顺序指定它们更不容易出错。在我从事的实际应用程序中,通常我们将 SQL 存储在与 DAO 代码不同的文件中,并且可能很容易意外地以错误的顺序获取参数。

【讨论】:

是否可以在同一个数据源中同时使用 NamedParameterJdbcTemplate 和 JdbcTemplate?他们会互相干扰吗? @Max: 没有共享状态 @MaxG Spring JdbcTemplate 在 NamedParameterJdbcTemplate 中公开,以允许通过 getJdbcOperations() 方法从底层 JdbcTemplate 调用方法。所以你不需要同时创建。 在实际应用程序中,SQL 通常存储在与 DAO 不同的文件中”我同意这是一个好方法,但是我从未见过跨度>

以上是关于NamedParameterJdbcTemplate使用详解的主要内容,如果未能解决你的问题,请参考以下文章