JdbcTemplate

Java开发中,JDK自带的JDBC一直以来都因设计比较繁琐而饱受诟病(尤其是其对受检查异常的滥用和冗长的数据到对象的映射写法),对于稍大的项目,直接使用JDBC进行数据库操作开发效率不高。Spring提供了封装的JDBC模块JdbcTemplate,使用它能大大简化开发人员对数据库操作的代码。

例子数据准备

这里我们以例子的形式进行介绍,例子的ER图如下图所示。

设计中共有学生、课程、教师三个实体类,学生和课程为多对多关系,教师和课程为一对多关系。

传统Spring工程引入和配置JdbcTemplate

在传统Spring工程中使用JdbcTemplate,我们需要引入如下依赖。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.19</version>
</dependency>

applicationContext.xml中,我们需要配置数据源。

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
    <property name="poolName" value="mercatus_connection_pool"/>
    <property name="dataSourceClassName"
              value="com.mysql.cj.jdbc.MysqlDataSource"/>
    <property name="maximumPoolSize" value="50"/>
    <property name="maxLifetime" value="60000"/>
    <property name="idleTimeout" value="30000"/>
    <property name="dataSourceProperties">
        <props>
            <prop key="url">${jdbc.url}</prop>
            <prop key="user">${jdbc.username}</prop>
            <prop key="password">${jdbc.password}</prop>
            <prop key="prepStmtCacheSize">250</prop>
            <prop key="prepStmtCacheSqlLimit">2048</prop>
            <prop key="cachePrepStmts">true</prop>
            <prop key="useServerPrepStmts">true</prop>
        </props>
    </property>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <constructor-arg ref="hikariConfig"/>
</bean>

此外在applicationContext.xml中还需要装配JdbcTemplate的Bean。

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="dataSource" />
</bean>

如上配置好后,就可以在工程中使用JdbcTemplate了。

SpringBoot引入和配置JdbcTemplate

在SpringBoot中使用JdbcTemplate非常简单,首先我们需要在pom.xml中引入相关的起步依赖。

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

此外还需要配置数据源,SpringBoot中配置数据源有多种方式,这里我们直接采用在application.properties中进行配置。

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/stums?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&socketTimeout=30000&connectTimeout=3000&queryTimeoutKillsConnection=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=120
spring.datasource.hikari.idle-timeout=60000

手动映射查询结果集到实体对象

下面例子中我们编写了两个数据访问层类,实现如下几个示例功能:

  • 按id查询学生
  • 查询所有学生
  • 查询某个学生选的所有课程
  • 查询某个课程的所有学生
  • 查询某个学生的所有老师

StudentDao.java

package com.gacfox.demo.demojdbc.dao;

import com.gacfox.demo.demojdbc.model.Student;
import com.gacfox.demo.demojdbc.model.Teacher;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component("studentDao")
public class StudentDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 根据学生ID查询学生信息
     *
     * @param id 学生ID
     * @return 学生对象
     */
    public Student queryStudentById(Long id) {
        String sql = "select * from t_student where student_id = ?";

        Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql, id);
        Student student = new Student();
        student.setStudentId((Long) resultMap.get("student_id"));
        student.setStudentName((String) resultMap.get("student_name"));
        return student;
    }

    /**
     * 查询所有学生
     *
     * @return 包含所有学生对象的列表
     */
    public List<Student> queryAllStudents() {
        String sql = "select * from t_student order by student_id asc";
        List<Student> studentList = new ArrayList<>();
        List<Map<String, Object>> mapList;
        mapList = jdbcTemplate.queryForList(sql);
        for (Map<String, Object> m : mapList) {
            Student student = new Student();
            student.setStudentId((Long) m.get("student_id"));
            student.setStudentName((String) m.get("student_name"));
            studentList.add(student);
        }
        return studentList;
    }

    /**
     * 查询某个学生选的所有课
     *
     * @param id 学生ID
     * @return 包含课程对象的列表
     */
    public List<com.gacfox.demo.demojdbc.model.Class> queryAllClassesByStudentId(Long id) {
        String sql = "select distinct t_class.class_id,t_class.class_name,t_class.teacher_id " +
                "from t_class " +
                "inner join t_student_class on t_class.class_id=t_student_class.class_id " +
                "where t_student_class.student_id = ?";
        List<com.gacfox.demo.demojdbc.model.Class> classList = new ArrayList<>();
        List<Map<String, Object>> mapList;
        mapList = jdbcTemplate.queryForList(sql, id);
        for (Map<String, Object> m : mapList) {
            com.gacfox.demo.demojdbc.model.Class c = new com.gacfox.demo.demojdbc.model.Class();
            c.setClassId((Long) m.get("class_id"));
            c.setClassName((String) m.get("class_name"));
            classList.add(c);
        }
        return classList;
    }

    /**
     * 查询某个学生的所有教师
     *
     * @param id 学生ID
     * @return 包含教师对象的列表
     */
    public List<Teacher> queryTeachersByStudentId(Long id) {
        String sql = "select distinct t_teacher.teacher_id,t_teacher.teacher_name from t_teacher " +
                "inner join t_class on t_teacher.teacher_id = t_class.teacher_id " +
                "inner join t_student_class on t_class.class_id = t_student_class.class_id " +
                "where t_student_class.student_id = ?";
        List<Teacher> teacherList = new ArrayList<>();
        List<Map<String, Object>> mapList;
        mapList = jdbcTemplate.queryForList(sql, id);
        for (Map<String, Object> m : mapList) {
            Teacher teacher = new Teacher();
            teacher.setTeacherId((Long) m.get("teacher_id"));
            teacher.setTeacherName((String) m.get("teacher_name"));
            teacherList.add(teacher);
        }
        return teacherList;
    }
}

ClassDao.java

package com.gacfox.demo.demojdbc.dao;

import com.gacfox.demo.demojdbc.model.Student;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component("classDao")
public class ClassDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询某门课程的所有学生
     *
     * @param id 课程ID
     * @return 包含学生对象的列表
     */
    public List<Student> queryStudentsByClassId(Long id) {
        String sql = "select t_student.student_id,t_student.student_name from t_student " +
                "inner join t_student_class on t_student.student_id = t_student_class.student_id " +
                "where t_student_class.class_id=?";
        List<Student> studentList = new ArrayList<>();
        List<Map<String, Object>> mapList;
        mapList = jdbcTemplate.queryForList(sql, id);
        for (Map<String, Object> m : mapList) {
            Student student = new Student();
            student.setStudentId((Long) m.get("student_id"));
            student.setStudentName((String) m.get("student_name"));
            studentList.add(student);
        }
        return studentList;
    }
}

为了方便演示,代码中,未对具有关联关系的实体进行关联查询赋值,实际情况下应根据具体需求实现。从代码中我们可以看到,JdbcTemplate提供给我们了许多重载方法,可以用许多种不同的方式传递参数和获取结果,以上功能可以用许多不同的写法实现,我们的项目中选一种就可以了,不要写的乱七八糟。上面代码中,查询主要用了两种方法queryForMapqueryForList,分别用于查询单个结果和多个结果列表。

自动映射查询结果集到实体对象

前面代码中我们将结果集映射到实体对象的操作是手动进行的,虽然灵活性更高但比较麻烦,实际上SpringJDBC也提供了自动映射的功能。注意,自动映射时实体类的属性名和数据库中的名字要相同,比如数据库字段student_id对应属性名studentId

//自动映射单个结果
student = jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(Student.class));
//自动映射多个结果
studentList = jdbcTemplate.query(sql, new Object[]{id}, new BeanPropertyRowMapper<>(Student.class));

query函数其中的一个参数是RowMapper<T>,这个参数就是用来告诉JdbcTemplate将结果集映射到哪个实体类的。

使用execute()和update()

除了查询,对于其他的数据库操作:增、删、改,可以使用JdbcTemplate提供的update()方法,对于其他的操作,比如要用代码建表,可以使用execute()方法。

public int update(String sql,
         Object... args)
           throws org.springframework.dao.DataAccessException

update()中,参数是SQL语句和SQL参数,返回值是受影响的列数。

public void execute(String sql)
             throws org.springframework.dao.DataAccessException

execute()中,参数是SQL语句。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap