为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。 作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。
一、
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
- 数据脚本
CREATE DATABASE db1022; USE db1022; CREATE TABLE book ( isbn VARCHAR (50) PRIMARY KEY COMMENT '书号', book_name VARCHAR (100) COMMENT '书名', price INT COMMENT '价格' ) ; CREATE TABLE book_stock ( isbn VARCHAR (50) PRIMARY KEY COMMENT '书号', stock INT ) ; CREATE TABLE account ( username VARCHAR (50) PRIMARY KEY COMMENT '用户名', balance INT COMMENT '用户余额' ) ; INSERT INTO account (`username`,`balance`) VALUES ('Tom',300); INSERT INTO account (`username`,`balance`) VALUES ('Jerry',400); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-001','book01',100); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-002','book02',200); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-001',10); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-002',20);
二、
- 创建 jdbc.properties
user=root password=root jdbcUrl=jdbc:mysql:///db1022?useSSL=false&serverTimezone=UTC driverClass=com.mysql.jdbc.Driver initialPoolSize=30 minPoolSize=10 maxPoolSize=100 acquireIncrement=5 maxStatements=1000 maxStatementsPerConnection=10
- application_jdbcTemplate.xml
<context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${user}"/> <property name="password" value="${password}"/> <property name="jdbcUrl" value="${jdbcUrl}"/> <property name="driverClass" value="${driverClass}"/> <property name="initialPoolSize" value="${initialPoolSize}"/> <property name="minPoolSize" value="${minPoolSize}"/> <property name="maxPoolSize" value="${maxPoolSize}"/> <property name="acquireIncrement" value="${acquireIncrement}"/> <property name="maxStatements" value="${maxStatements}"/> <property name="maxStatementsPerConnection" value="${maxStatementsPerConnection}"/> </bean> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
三、
@AllArgsConstructor @NoArgsConstructor @Data @ToString public class Book { private String book_name; private String price; }
public class JDBCTemplate_Test { private ApplicationContext ctx = null; private JdbcTemplate jdbcTemplate = null; { ctx = new ClassPathXmlApplicationContext("application_jdbcTemplate.xml"); jdbcTemplate = (JdbcTemplate) ctx.getBean("template"); } /** * 执行 INSERT */ @Test public void testInsert() { String sql = "INSERT INTO book (`isbn`,`book_name`,`price`) VALUES (?,?,?);"; jdbcTemplate.update(sql, "ISBN", "ISBN", 100); } /** * 执行UPDATE */ @Test public void testUpdate() { String sql = "UPDATE book SET book_name = ? WHERE price = ?"; jdbcTemplate.update(sql,"book01",100); } /** * 执行 DELETE */ @Test public void testDelete() { String sql = "DELETE from book WHERE book_name = ?"; jdbcTemplate.update(sql, "book01"); } /** * 测试批量更新操作 最后一个参数是 Object[] 的 List 类型:因为修改一条记录需要一个 Object 数组,修改多条记录就需要一个 * List 来存放多个数组。 */ @Test public void testBatchUpdate() { String sql = "INSERT INTO book(isbn, book_name, price) VALUES(?, ?, ?)"; List<Object[]> batchArgs = new ArrayList<>(); batchArgs.add(new Object[] { "ISBN-006", "book06", "600" }); batchArgs.add(new Object[] { "ISBN-007", "book07", "700" }); batchArgs.add(new Object[] { "ISBN-008", "book08", "800" }); batchArgs.add(new Object[] { "ISBN-009", "book09", "900" }); jdbcTemplate.batchUpdate(sql, batchArgs); } /** * 从数据库中获取一条记录,实际得到对应的一个对象 注意:不是调用 queryForObject(String sql,Class<Employee> requiredType, Object... args) 方法! * 而需要调用queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args) * 1、其中的 RowMapper 指定如何去映射结果集的行,常用的实现类为 BeanPropertyRowMapper * 2、使用SQL中的列的别名完成列名和类的属性名的映射,例如 last_name lastName * 3、不支持级联属性。 JdbcTemplate只能作为一个 JDBC 的小工具, 而不是 ORM 框架 */ @Test public void testQueryForObject() { String sql = "SELECT * FROM book WHERE isbn=?"; RowMapper<Book> rowMapper = new BeanPropertyRowMapper<>(Book.class); //在将数据装入对象时需要调用set方法。 Book book = jdbcTemplate.queryForObject(sql, rowMapper, "ISBN-003"); System.out.println(book); } /** * 一次查询多个对象 * 注意:调用的不是 queryForList 方法 */ @Test public void testQueryForList() { String sql = "SELECT * FROM book WHERE isbn like ?"; RowMapper<Book> rowMapper = new BeanPropertyRowMapper<>(Book.class); List<Book> bookList = jdbcTemplate.query(sql, rowMapper, "%ISBN%"); if (!CollectionUtils.isEmpty(bookList)) { bookList.forEach(user -> { System.out.println(user); }); } } /** * 获取单个列的值或做统计查询 * 使用 queryForObject(String sql, Class<Long> requiredType) */ @Test public void testQueryForCount() { String sql = "SELECT count(isbn) FROM book"; long count = jdbcTemplate.queryForObject(sql, Long.class); System.out.println(count); } }
四、使用具名参数的NamedParameterJdbcTemplate
关于具名参数:
String sql = "SELECT * FROM book WHERE isbn=?";由上面可以看出使用?作为展位符,在填充参数的时候要考虑问号的顺序。具名参数很好的解决了这个问题,是我们在填充参数时不用考虑参数顺序
在Spring中可以通过NamedParameterJdbcTemplate类的对象使用带有具名参数的SQL语句。
4.1、通过IOC容器创建NamedParameterJdbcTemplate对象
<!-- 配置可以使用具名参数的JDBCTemplate类对象 --> <bean id="namedTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <!-- 没有无参构造器,必须传入数据源或JdbcTemplate对象 --> <constructor-arg ref="dataSource"/>
4.2、具名参数传入方式有两种
(1)、通过Map对象传入
NamedParameterJdbcTemplate.update(String sql, Map<String, ?> map)
Map的键是参数名,值是参数值
(2)、通过SqlParameterSource对象传入
public class NamedParameterJdbcTemplate_Test { private ApplicationContext ctx = null; private NamedParameterJdbcTemplate template = null; { ctx = new ClassPathXmlApplicationContext("application_jdbcTemplate.xml"); template = (NamedParameterJdbcTemplate) ctx.getBean("namedTemplate"); } /** * 通过Map对象传入 */ @Test public void test_Method01(){ //:p、:bn 具名参数名字随便起,冒号+名字 String sql = "UPDATE book SET price = :p WHERE book_name = :bn"; Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("bn","book01"); paramMap.put("p","1000"); System.out.println(template.update(sql, paramMap)); } /** * 通过SqlParameterSource对象传入 */ @Test public void test_Method02(){ //模拟Service曾直接传递给Dao曾一个具体的对象。 String sql = "UPDATE book SET price = :price WHERE book_name = :book_name"; Book book = new Book("book01","10000"); //此时剧名参数名称必须好对象的属性名称保持一致 SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(book); System.out.println(template.update(sql, sqlParameterSource)); } }
五、使用JdbcTemplate实现Dao
通过IOC容器自动注入:JdbcTemplate类是线程安全的,所以可以在IOC容器中声明它的单个实例,并将这个实例注入到所有的Dao实例中。
@Repository public class EmployeeDao { @Autowired private JdbcTemplate jdbcTemplate; public Employee get(Integer id){ //… } }