| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 一文学会Spring JDBC 使用 -> 正文阅读 |
|
[Java知识库]一文学会Spring JDBC 使用 |
Spring JDBC1、JDBCJDBC 就是 数据库开发 操作的 代名词,因为只要是现代商业项目的开发那么一定是离不开 数据库 的,不管你搞的是什么,只要是想使用动态的开发结构,那么一定就是 JDBC ,那么下面首先来回顾一下传统JDBC的使用。 JDBC有四种连接: 像JDBC-ODBC的连接已经确定不再使用了、主要采用的是?JDBC网络连接模式?。
? 从历史的发展上来讲,ORMapping组件出现较多:JDO、Entity Bean、Hibernate、IBatis、SpringJDBC、MyBatis、JPA标准,当然随着技术的发展与淘汰,基本上现在阶段剩下的ORM组件,常用的就是MyBatis(国内互联网公司)、JPA(国外机构),而SpringJDBC是属于JDBC的轻度包装组件(其他的组件都属于重度包装),所以使用? 添加依赖 <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>5.3.21</spring.version> <mysql.version>8.0.30</mysql.version> </properties> <dependencies> <!--核心依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <!--spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <!--数据库依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--测试--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.8.2</version> </dependency> <!--日志依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!--日志依赖--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> </dependencies> log4j.properties日志配置文件 (当启动程序,没有任何报错,但是没有信息打印时,需要配置日志) #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Threshold=DEBUG log4j.appender.console.ImmediateFlush=true log4j.appender.console.Target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/logFile.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG 2、使用要想使用JDBC,配置数据源,是关键性的一步。 2.1、配置数据源:2.1.1、注册数据源对像 创建数据源的配置类:(基于配置类的方式) import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; @Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { // 驱动数据源 DriverManagerDataSource dataSource = new DriverManagerDataSource(); // 加载驱动程序 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/yootk"); dataSource.setUsername("root"); dataSource.setPassword("317311"); return dataSource; } } 创建数据源的配置类:(基于xml的方式) <?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--数据源的配置--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/yootk"/> <property name="username" value="root"/> <property name="password" value="317311"/> </bean> </beans> 2.1.2、测试: import look.word.jdbc.config.DataSourceConfig; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; @ContextConfiguration(classes = DataSourceConfig.class) // 两者二选一即可 //@ContextConfiguration(locations ={"classpath:data-source.xml"}) @ExtendWith(SpringExtension.class) public class TestDataSource { // 日志工厂对象 private static final Logger LOGGER = LoggerFactory.getLogger(TestDataSource.class); @Autowired private DataSource dataSource; @Test public void testConnection() throws Exception{ LOGGER.info("【数据库连接对象】:{}",dataSource); } } // 执行结果 输入数据源对象,说明连接成功 // [INFO ] 2022-09-14 12:18:59,307(386) --> [main] look.word.test.TestDataSource.testConnection(TestDataSource.java:29): 【数据库连接对象】:org.springframework.jdbc.datasource.DriverManagerDataSource@535779e4 但是基于这种连接操作的性能是非常一般的,请追随源代码,一探究竟。 然后找到我们的AbstractDriverBasedDataSource.getConnection()方法,进入getConnectionFromDriver()方法。 找到getConnectionFromDriver(),他是一个抽象方法,然后找到其子类,DriverManagerDataSource 然后又会发现,我们回到了DriverManagerDataSource,然后我们在进入getConnectionFromDriverManager方法。 最终获取连接的方式, 2.1.3、默认连接方式的缺点 ? 这种连接的管理方式,是在每一次 获取连接 的时候 才进行 数据库连接的操作了,那么现在的问题就来了,这样的管理方式好吗 ?首先在数据库连接的处理之中,一定会建立若干个Socket 连接,那么会有?耗时?,而在数据库关闭的时候也会存在有同样的耗时处理,这样在“次次次高并发”的处理下很难得到有效的控制。?所以在实际项目中最佳数据库连接的管理,一定是基于数据库连接池方式实现的?。所以此时可以考虑在 Spring 内部去实现一个连接池的维护。早期的数据库连接池组件提供有一个 C3P0组件,但是现在已经停止维护了。 2.2、HikariCP? 在实际的项目应用开发过程之中,为了解决?JDBC连接与关闭的延时以及性能问题?,提供了?数据库连接池?的解决方案,并且针对于该方案提供了成型的HikariCP服务组件。HikariCP (Hikari来自日文,是“光”的含义)是由日本程序员开源的一个数据库连接池组件,该组件拥有如下特点:
? 在Spring之中默认推荐的数据库连接池组件就是?HikariCP?,不建议再使用其他的数据库连接池组件,当然国内也有优秀的CP组件,那么就是阿里推出的Druid(在性能上可能低于HikariCP,但是提供有完整的管理界面),如果要想使用这个组件,可以采用如下的步骤进行配置。 2.2.1、使用 添加依赖: <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> 编写配置类: 这次我们再用配置文件的方式,方便扩展
yootk.database.driverClassName=com.mysql.cj.jdbc.Driver yootk.database.jdbcUrl=jdbc:mysql://localhost:3306/yootk yootk.database.username=root yootk.database.password=317311 # 【Hikaricp】配置数据库连接超时时间 单位【毫秒】 yootk.database.connectionTimeOut=3000 # 【Hikaricp】一个连接最小维持的时间 单位【毫秒】 yootk.database.idleTimeOut=3000 # 【Hikaricp】一个连接最长存活的时间 单位【毫秒】 yootk.database.maxLifetime=6000 # 【Hikaricp】最大保存的数据库连接实例 yootk.database.maximumPoolSize=60 # 【Hikaricp】最小保存的数据库连接实例 (在没有任何用户访问时,最少维持的连接数量) yootk.database.minimumIdle=20 # 【Hikaricp】是否为只读 yootk.database.readOnly=false 创建配置对象 @Configuration //读取指定位置的资源文件 @PropertySource("classpath:config/database.properties") public class HikariCpDataSourceConfig { /** * 绑定资源文件中的配置数据项 */ @Value("${yootk.database.driverClassName}") private String driverClassName; @Value("${yootk.database.jdbcUrl}") private String jdbcUrl; @Value("${yootk.database.username}") private String username; @Value("${yootk.database.password}") private String password; @Value("${yootk.database.connectionTimeOut}") private Long connectionTimeOut; @Value("${yootk.database.idleTimeOut}") private Long idleTimeOut; @Value("${yootk.database.maxLifetime}") private Long maxLifetime; @Value("${yootk.database.maximumPoolSize}") private Integer maximumPoolSize; @Value("${yootk.database.minimumIdle}") private Integer minimumIdle; @Value("${yootk.database.readOnly}") private boolean readOnly; @Bean("dataSource") public DataSource dataSource() { // Hikari连接池数据源 HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setJdbcUrl(jdbcUrl); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setPassword(password); // 超时时间 dataSource.setConnectionTimeout(connectionTimeOut); // 空闲超时 dataSource.setIdleTimeout(idleTimeOut); // 连接的最长时间 dataSource.setMaxLifetime(maxLifetime); // 连接池最大数量 dataSource.setMaximumPoolSize(maximumPoolSize); // 当没有连接时 最小保留的连接数量 dataSource.setMinimumIdle(minimumIdle); // 是否只读数据库 dataSource.setReadOnly(readOnly); return dataSource; } } 测试类: import look.word.jdbc.config.HikariCpDataSourceConfig; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; @ContextConfiguration(classes = HikariCpDataSourceConfig.class) @ExtendWith(SpringExtension.class) public class TestDataSource { private static final Logger LOGGER = LoggerFactory.getLogger(TestDataSource.class); @Autowired private DataSource dataSource; @Test public void testConnection() throws Exception { LOGGER.info("【数据库连接对象】:{}", dataSource.getConnection()); } } ? 如果出错,可以看看日志输入信息。 这样我们就实现了,使用HikariCP获取连接对象了,接下来就会使用HikariCP对具体的数据库进行操作。 2.3、JdbcTempLateJdbcTempLate的使用很简单,只需要为其指定数据源即可。 我们采用配置类的方式,为其配置数据源 2.3.1、增 添加配置类: @Configuration public class JdbcTempLateConfig { @Bean // 方法形参 会自动从容器中注入对象 public JdbcTemplate jdbcTemplate(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate; } } 编写测试类: @ContextConfiguration(classes = {HikariCpDataSourceConfig.class, JdbcTempLateConfig.class}) @ExtendWith(SpringExtension.class) public class TestJdbcTempLate { private static final Logger LOGGER = LoggerFactory.getLogger(TestJdbcTempLate.class); @Autowired private JdbcTemplate jdbcTemplate; @Test public void testConnection() throws Exception { String sql = "insert into book(title,author,price) values('java入门','李老师',99.90)"; LOGGER.info("【插入执行结果】:{}", jdbcTemplate.update(sql)); } } 执行结果: 这个时候就是用JdbcTemplate轻松地实现了数据的插入操作。 但是,可以发现,我们上面的操作,还是存在问题的,比如没有对sql 进行预处理,会出现 Sql 注入的风险。 2.3.2、改 测试类 @Test public void testUpdate() { String sql = "update yootk.book set title = ? where bid = ?"; LOGGER.info("【插入执行结果】:{}", jdbcTemplate.update(sql, "Python入门", 2)); } 2.3.3、删 测试类 @Test public void testDelete() { String sql = "delete from yootk.book where bid = ?"; LOGGER.info("【插入执行结果】:{}", jdbcTemplate.update(sql, 2)); } 2.3.4、增 (返回id) ? 在MySQL数据库里面,有一种功能,可以通过一个next()处理函数获取当前所生成的ID号(主要针对于自动增长列),实际上这个功能主要的目的是为了解决增加数据时的ID返回处理问题了,因为很多的时候需要在数据增加成功之后对指定的ID进行控制,所以才提供了专属的处理函数,Oracle之中直接使用序列即可,但是MySQL的实现就需要专属的处理函数了。.在程序的开发之中,如果要想获取到增长后的ID数据,在SpringJDBC里面提供有了一个?KeyHolder?接口,在这个接口里面定义了?获取主键?内容的处理方法。 ? 在平常开发中,我们经常会遇到,插入这个数据后,会需要这个数据的id,然后对其进行一系类操作。 ? 如果要想获取到增长后的ID数据,在SpringJDBC里面提供有了一个?KeyHolder?接口,在这个接口里面定义了获取主键内容的处理方法。 测试类 @Test public void testInsertReturnId() { String sql = "insert into yootk.book(title,author,price) values(?,?,?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); // 获取KEY的处理信息 int count = jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 对sql进行预处理 ps.setString(1, "Springboot实战"); ps.setString(2, "老李"); ps.setDouble(3, 99.00); return ps; } }, keyHolder); LOGGER.info("【插入执行影响行数】:{},当前插入数据的ID:{}", count, keyHolder.getKey()); } // 执行结果 // look.word.test.TestJdbcTempLate.testInsertReturnId(TestJdbcTempLate.java:61): 【插入执行影响行数】:1,当前插入数据的ID:4 如果在? 2.3.5、批处理 测试类: 这种方式是基于集合的。 @Test public void testInsertBatch() { List<String> titles = List.of("Springboot开发实战", "SSM开发案例", "Netty开发实战", "Redis开发实战"); List<Double> prices = List.of(90.1, 98.9, 78.9, 98.9); String sql = "insert into yootk.book(title,author,price) values(?,?,?)"; this.jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { // 执行批量插入 //@param i 集合索引 @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, titles.get(i)); ps.setString(2, "老李老师"); ps.setDouble(3, prices.get(i)); } @Override public int getBatchSize() { return titles.size(); //总长度 } }); } 基于对象 @Test public void testInsertBatch2() { List<Object[]> params = List.of( new Object[]{"Spring开发实战", "11", 89.0}, new Object[]{"Spring开发实战1", "11", 89.0}, new Object[]{"Spring开发实战2", "11", 89.0}, new Object[]{"Spring开发实战3", "11", 89.0} ); String sql = "insert into yootk.book(title,author,price) values(?,?,?)"; int[] result = jdbcTemplate.batchUpdate(sql, params);//批量插入 System.out.println("result = " + result); } 2.3.4、查 ? 在数据库操作过程中,除了数据更新操作之外,最为繁琐的就是数据库的查询功能了。由于JdbcTemplate设计的定位属于ORMapping组件,所以就需要在查询完成之后,可以自动的将查询结果转为VO类型的实例,而为了解决该问题,在SpringJDBC中提供了一个RowMapper接口,这个接口可以实现ResultSet向指定对象实例的转换。该接口提供有一个mapRow()处理方法,可以接收查询结果每行数据的结果集,用户可以将指定列取出,并保存在自标VO实例之中 查询单个 Book 对象 根据数据库创建 @Data @AllArgsConstructor @NoArgsConstructor public class Book { private Integer bid; private String title; private String author; private Double price; } 测试类: // 查询单个 @Test public void testQuery() { String sql = "select bid, title, author, price from yootk.book where bid = ?"; Book book = jdbcTemplate.queryForObject(sql, new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setBid(rs.getInt(1)); book.setTitle(rs.getString(2)); book.setAuthor(rs.getString(3)); book.setPrice(rs.getDouble(4)); return book; } }, 3); // 这里的3 是对预处理数据的回填 多个需按照顺序编写 System.out.println("【queryForObject 查询结果】book = " + book); } 查询多个 // 查询所有 @Test public void testQueryAll() { String sql = "select bid, title, author, price from yootk.book "; List<Book> list = jdbcTemplate.query(sql, new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setBid(rs.getInt(1)); book.setTitle(rs.getString(2)); book.setAuthor(rs.getString(3)); book.setPrice(rs.getDouble(4)); return book; } }); list.stream().forEach(System.out::println); } 分页查询 // 分页 @Test public void testQuerySpAll() { int current = 2; // 页数 int size = 5;// 每页数量 String sql = "select bid, title, author, price from yootk.book limit ? ,? "; List<Book> list = jdbcTemplate.query(sql, new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setBid(rs.getInt(1)); book.setTitle(rs.getString(2)); book.setAuthor(rs.getString(3)); book.setPrice(rs.getDouble(4)); return book; } }, (current - 1) * size, size); list.stream().forEach(System.out::println); } 统计行数 // 查询行数 @Test public void testQueryCount() { String sql = "select count(*) from yootk.book where title like ?"; long count = jdbcTemplate.queryForObject(sql, new RowMapper<Long>() { @Override public Long mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getLong(1); } }, "%Spring%"); LOGGER.info("【数据库记录总行数】{}", count); } |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 9:33:04- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |