spring-jdbc,在spring中如何更加高效的使用JDBC
春季JDBC1、JDBC就是数据库开发操作的代名词,因为只要是现代商业项目的开发那么一定是离不开数据库的,不管你搞的是什么,只要是想使用动态的开发结构,那么一定就是JDBC,那么下面首先来回顾一下传统数据库编程的使用。
数据库编程有四种连接:像JDBC-ODBC的连接已经确定不再使用了、主要采用的是数据库编程网络连接模式。
在数据库编程的开发之中,一定要配置相应数据库的驱动程序后才可以使用,所以这就属于标准的做法,同时还有一点必须明确,不管未来出现了什么样的爪哇数据库开发框架,那么核心的本质只有一点:JDBC,可是数据库编程标准里面所定义的操作结构是属于较为底层的操作形式,所以使用起来非常的繁琐,因为几乎所有的数据库的项目都需要加载驱动、创建数据库连接、数据库的操作对象、关闭数据库,只有中间的数据库的创建、读取、更新和删除操作是有区别的,那么就需要考虑对数据库编程进行封装了,那么这个时候就有了对象关系映射(对象关系映射)组件(全称或测绘,对象关联映射,采用对象的形式实现数据库编程的开发操作)。
从历史的发展上来讲,或映射组件出现较多:JDO,实体Bean、Hibernate、IBatis、SpringJDBC、MyBatis、JPA标准,当然随着技术的发展与淘汰,基本上现在阶段剩下的对象关系映射(对象关系映射)组件,常用的就是米巴蒂斯(国内互联网公司)、JPA(国外机构),而SpringJDBC是属于数据库编程的轻度包装组件(其他的组件都属于重度包装),所以使用SpringJDBC可以简化数据库编程传统开发里面繁琐的操作步骤。
添加依赖性能
17 /maven .编译器. source
17 /maven .编译器. target
UTF-8
5.3.21版/spring.version
MySQL。8 .0 .30版/mysql.version
/属性
属国
!-核心依赖-
属国
groupId组织。spring框架/groupId
工件id spring-上下文/工件id
/依赖关系
!-春天-jdbc -
属国
groupId组织。spring框架/groupId
工件id春天-JDBC/工件id
/依赖关系
!-数据库依赖-
属国
groupId mysql /groupId
artifactId MySQL-连接器-java /artifactId
/依赖关系
!-测试-
属国
groupId组织。朱尼特。Jupiter/groupId
artifactId JUnit-Jupiter-API/artifactId
版本5 .8 .2/版本
/依赖关系
!-日志依赖-
属国
groupId组织。lombok/groupId项目
artifactId lombok /artifactId
版本1 .18 .24/版本
/依赖关系
属国
groupId org.slf4j /groupId
artifactId slf4j-api /artifactId
版本1 .7 .25/版本
/依赖关系
!-日志依赖-
属国
groupId org.slf4j /groupId
工件id slf4j-log4j 12/工件id
版本1 .7 .25/版本
/依赖关系
/dependencies log4j.properties日志配置文件(当启动程序,没有任何报错,但是没有信息打印时,需要配置日志)
#将等级为调试的日志信息输出到安慰和文件这两个目的地,控制台和文件的定义在下面的代码
log4j.rootLogger=调试,控制台,文件
#控制台输出的相关设置
log4j。阑尾手术。控制台=组织。阿帕奇。log4j。控制台附加器
log4j。阑尾手术。控制台。阈值=调试
log4j。阑尾手术。控制台。立即刷新=真
log4j。阑尾手术。控制台。目标=系统。犯罪
log4j。阑尾手术。控制台。布局=组织。阿帕奇。log4j。模型设计
log4j。阑尾手术。控制台。布局。转换模式=[%-5p]% d(% r)-[% t]% l:% m % x % n
#文件输出的相关设置
log4j。阑尾手术。文件=组织。阿帕奇。log4j。滚动文件附加器
log4j.appender.file.File=./log/logFile.log
log4j。阑尾手术。文件。最大文件大小=10mb
log4j。阑尾手术。文件。阈值=调试
log4j。阑尾手术。文件。布局=组织。阿帕奇。log4j。模型设计
log4j。阑尾手术。文件。布局。转换模式=[% p][% d { YY-MM-DD }][% c]% m % n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j。伐木工。Java。SQL。语句=调试
log4j。伐木工。Java。SQL。结果集=调试
log4j。伐木工。Java。SQL。准备好的语句=调试2,使用要想使用JDBC,配置数据源,是关键性的一步。
2.1、配置数据源:2.1.1、注册数据源对像创建数据源的配置类:(基于配置类的方式)
导入org。spring框架。语境。注释。豆;
导入org。spring框架。语境。注释。配置;
导入org。spring框架。JDBC。数据来源。drivermanagerdatasource
导入javax。SQL。数据来源;
@配置
公共类DataSourceConfig {
@Bean
公共数据源数据源(){
//驱动数据源
DriverManagerDataSource数据源=new DriverManagerDataSource();
//加载驱动程序
数据来源。setdriver类名( com。MySQL。CJ。JDBC。司机’);
数据来源。seturl( JDBC:MySQL://localhost:3306/yootk );
数据来源。设置用户名(“root”);
数据来源。设置密码( 317311 );
返回数据源;
}
}创建数据源的配置类:(基于可扩展标记语言的方式)
?可扩展标记语言版本=1.0 编码=UTF八号?
豆子xmlns= http://www。spring框架。组织/架构/bean
xmlns:xsi= http://。w3。 org/2001/XML架构-实例
xmlns:p= http://www。spring框架。组织/架构/p
xmlns:c= http://。spring框架。 org/schema/c
xsi:架构位置= http://www。spring框架。组织/模式/bean
http://www.springframework.org/schema/beans/spring-beans.xsd
!-数据源的配置-
bean id=dataSource
属性名=driverClassName 值=com.mysql.cj.jdbc.Driver/
属性名=url 值= JDBC:MySQL://localhost:3306/yootk /
属性名=用户名值=根/
属性名=密码值=317311/
/bean
/bean 2。1 .2,测试:导入看啊。词。JDBC。配置。数据源配置;
导入org。朱尼特。木星。API。测试;
导入org。朱尼特。木星。API。扩展。用.扩展;
导入org。slf4j。记录者;
导入org。SLF 4j。伐木工厂;
导入org。spring框架。豆子。工厂。注释。自动连线;
导入org。spring框架。测试。语境。上下文配置;
导入org。spring框架。测试。语境。朱尼特。木星。弹簧拉伸;
导入javax。SQL。数据来源;
@ context配置(classes=数据源配置。class)//两者二选一即可
//@上下文配置(locations={ class path:data-source。XML })
@用(弹簧延伸)延伸。类)
公共类TestDataSource {
//日志工厂对象
私有静态最终记录器记录器=记录器工厂。获取记录器(测试数据源。类);
@自动连线
私有数据源数据来源;
@测试
公共void testConnection()引发异常{
LOGGER.info(【数据库连接对象】: {} ,数据来源);
}
}
//执行结果输入数据源对象,说明连接成功
//[信息] 2022-09-14 12:18:59,307(386)-[主]看。词。测试。测试数据源。测试连接(测试数据源。Java:29):【数据库连接对象】:org。spring框架。JDBC。数据来源。drivermanagerdatasource @ 535779 E4但是基于这种连接操作的性能是非常一般的,请追随源代码,一探究竟。
然后找到我们的abstractdriverbaseddata源。获取连接()方法,进入getConnectionFromDriver()方法。
找到getConnectionFromDriver(),他是一个抽象方法,然后找到其子类,DriverManagerDataSource
然后又会发现,我们回到了DriverManagerDataSource,然后我们在进入getConnectionFromDriverManager方法。
最终获取连接的方式,
2.1.3.默认连接模式的缺点。这种连接管理模式是每次获得连接就连接数据库,所以现在问题来了。这样的管理模式好吗?首先,在数据库连接的过程中,会建立多个Socket连接,这需要时间,关闭数据库时也会存在同样耗时的过程,所以在“次高并发”的过程下很难得到有效的控制。因此,在实际项目中,最好的数据库连接管理必须基于数据库连接池。这时候可以考虑在Spring中维护一个连接池。的早期数据库连接池组件提供了一个C3P0组件,但现在已经停止维护。
2.2.在实际项目应用开发过程中,HikariCP提供了数据库连接池的解决方案,解决JDBC连接和关闭的延迟和性能问题,并为该解决方案提供了成型的HikariCP服务组件。HikariCP(光来自日语,意思是“光”)是一个数据库连接池组件,由日本程序员开源。该组件具有以下特征:
Yu代码更简洁,可以在缓存中加入更多的程序代码;实现了无锁集合,减少了并发访问带来的资源竞争。使用自定义数组类型(FastList)代替ArrayList,提高了get()和remove()的运算性能。优化CPU的时间片算法,尽可能在一个时间片中完成所有的处理操作。Spring中默认推荐的数据库连接池组件是HikariCP。不建议使用其他数据库连接池组件。当然国内也有优秀的CP组件,所以是阿里推出的Druid(可能性能比HikariCP低,但是提供了完整的管理接口)。如果您想使用这个组件,您可以使用下列步骤来配置它。
2.2.1,使用:dependency添加依赖关系
groupId com.zaxxer /groupId
artifactId HikariCP /artifactId
版本5 . 0 . 1/版本
/dependency写配置类:这一次,我们将使用配置文件来方便扩展。
创建配置文件:src/main/profiles/dev/config/database . properties
yootk . database . driver class name=com . MySQL . CJ . JDBC . driver
yootk . database . JDBC URL=JDBC:MySQL://localhost:3306/yootk
yootk.database.username=root
yootk.database.password=317311
# [hikari CP]配置数据库连接超时单位[毫秒]
yootk . database . connection time out=3000
# [hikari CP]连接的最小时间单位[毫秒]
yootk . database . idle time out=3000
# [hikari CP]连接的最长存活时间单位[毫秒]
yootk . database . max lifetime=6000
# [hikaricp]保存的数据库连接实例的最大数量
yootk . database . maximum poolsize=60
# 【Hikaricp】保存的最小数据库连接实例(没有用户访问时保持的最小连接数)
yootk.database.minimumIdle=20
#[光CP]是只读的?
Yootk.database.readOnly=false创建配置对象@Configuration
//读取指定位置的资源文件
@ property source( class path:config/database . properties )
公共类HikariCpDataSourceConfig {
/**
*绑定资源文件中的配置数据项
*/
@ Value( $ { yootk . database . driver class name } )
私有字符串driverClassName
@ Value( $ { yootk . database . JDBC URL } )
私有字符串jdbcUrl
@ Value( $ { yootk . database . username } )
私有字符串用户名;
@ Value( $ { yootk . database . password } )
私有字符串密码;
@ Value( $ { yootk . database . connection time out } )
私有长连接超时;
@ Value( $ { yootk . database . idle time out } )
private Long idleTimeOut
@ Value( $ { yootk . database . max lifetime } )
私有Long maxLifetime
@ Value( $ { yootk . database . maximumpoolsize } )
私有整数maximumPoolSize
@ Value( $ { yootk . database . minimu midle } )
私有整数minimumIdle
@ Value( $ { yootk . database . readonly } )
私有布尔只读;
@Bean(dataSource )
公共数据源dataSource() {
//光连接池数据源
hikari data source data source=new hikari data source();
data source . setdriver class name(driver class name);
数据来源。设置JDBC网址(JDBC网址);
dataSource.setUsername(用户名);
数据源. setPassword(密码);
数据源. setPassword(密码);
//超时时间
数据来源。setconnectiontime out(连接超时);
//空闲超时
数据来源。setidletimeout(空闲超时);
//连接的最长时间
数据来源。setmaxlifetime(最大生存期);
//连接池最大数量
数据来源。setmaximumpoolsize(maximumPoolSize);
//当没有连接时最小保留的连接数量
数据来源。setminimumidle(minimumIdle);
//是否只读数据库
数据来源。set readOnly(只读);
返回数据源;
}
}测试类:导入look。词。JDBC。配置。hikaricpdatasourceconfig
导入org。朱尼特。木星。API。测试;
导入org。朱尼特。木星。API。扩展。用.扩展;
导入org。slf4j。记录者;
导入org。SLF 4j。伐木工厂;
导入org。spring框架。豆子。工厂。注释。自动连线;
导入org。spring框架。测试。语境。上下文配置;
导入org。spring框架。测试。语境。朱尼特。木星。弹簧拉伸;
导入javax。SQL。数据来源;
@上下文配置(classes=hikaricpdatasourceconfig。类)
@用(弹簧延伸)延伸。类)
公共类TestDataSource {
私有静态最终记录器记录器=记录器工厂。获取记录器(测试数据源。类);
@自动连线
私有数据源数据来源;
@测试
公共void testConnection()引发异常{
LOGGER.info(【数据库连接对象】: {} ,数据来源。getconnection());
}
} 如果出错,可以看看日志输入信息。
这样我们就实现了,使用HikariCP获取连接对象了,接下来就会使用HikariCP对具体的数据库进行操作。
2.3、JdbcTempLateJdbcTempLate的使用很简单,只需要为其指定数据源即可。
我们采用配置类的方式,为其配置数据源
2.3.1、增添加配置类:@配置
公共类JdbcTempLateConfig {
@Bean //方法形参会自动从容器中注入对象
公共JDBC模板JDBC模板(数据源数据源){
JDBC模板JDBC模板=新JDBC模板(数据来源);
返回使用
}
}编写测试类:@上下文配置(classes={ hikaricpdatasourceconfig。class,JdbcTempLateConfig.class})
@用(弹簧延伸)延伸。类)
公共类TestJdbcTempLate {
私有静态最终记录器记录器=记录器工厂。获取记录器(testjdbctemplate。类);
@自动连线
私有JdbcTemplate
@测试
公共void testConnection()引发异常{
字符串sql=插入图书(标题、作者、价格)值(java入门,李老师,99.90);
LOGGER.info(【插入执行结果】: {} ,JDBC模板。update(SQL));
}
}执行结果:
这个时候就是用使用轻松地实现了数据的插入操作。
但是,可以发现,我们上面的操作,还是存在问题的,比如没有对结构化查询语言进行预处理,会出现结构化查询语言注入的风险。
2.3.2、改测试类@测试
public void testUpdate() {
string SQL= update yootk。书集标题=?where bid=?;
LOGGER.info(【插入执行结果】: {} ,jdbcTemplate.update(sql, Python入门, 2));
}2.3.3、删测试类@测试
public void testDelete() {
字符串SQL= delete from yootk。 book where bid=?;
LOGGER.info(【插入执行结果】: {} ,jdbcTemplate.update(sql,2));
}2.3.4、增(返回id)在关系型数据库数据库里面,有一种功能,可以通过一个下一个()处理函数获取当前所生成的身份号(主要针对于自动增长列),实际上这个功能主要的目的是为了解决增加数据时的身份返回处理问题了,因为很多的时候需要在数据增加成功之后对指定的身份进行控制,所以才提供了专属的处理函数,甲骨文之中直接使用序列即可,但是关系型数据库的实现就需要专属的处理函数了。在程序的开发之中,如果要想获取到增长后的身份数据,在SpringJDBC里面提供有了一个持钥匙者接口,在这个接口里面定义了获取主键内容的处理方法。
在平常开发中,我们经常会遇到,插入这个数据后,会需要这个数据的id,然后对其进行一系类操作。
如果要想获取到增长后的身份数据,在SpringJDBC里面提供有了一个持钥匙者接口,在这个接口里面定义了获取主键内容的处理方法。
测试类@测试
public void testInsertReturnId(){
字符串SQL= insert into yootk。书(书名、作者、价格)值(?);
生成的key holder key holder=新生成的key holder();//获取键的处理信息
整数=JDBC模板。update(new PreparedStatementCreator(){
@覆盖
公共准备语句createPreparedStatement(连接条件)引发SQLException {
预准备语句PS=con . prepare语句(SQL,语句RETURN _ GENERATED _ KEYS);//对结构化查询语言进行预处理
ps.setString(1, Springboot实战);
ps.setString(2,’老李);
ps.setDouble(3,99.00);
返回PS;
}
}、钥匙持有人);
LOGGER.info(【插入执行影响行数】:{},当前插入数据的ID:{} ,计数,密钥持有者。getkey());
}
//执行结果
//看。词。测试。测试JDBC模板。testinsertreturnid(测试JDBC模板。Java:61):【插入执行影响行数】:1,当前插入数据的ID:4如果在预准备语句PS=con . prepare语句(SQL);中,没有指定需要返回钥匙,则会出现异常。
2.3.5、批处理
测试类:这种方式是基于集合的。
@测试
public void testInsertBatch() {
列表字符串标题=List.of(Springboot开发实战,‘SSM开发案例,内蒂开发实战, Redis开发实战);
List Double prices=List.of(90.1,98.9,78.9,98.9);
字符串SQL= insert into yootk。书(书名、作者、价格)值(?);
这个。JDBC模板。批量更新(SQL,new BatchPreparedStatementSetter(){//执行批量插入
//@param i集合索引
@覆盖
公共空集值(预准备语句PS,int i)引发SQLException {
ps.setString(1,标题。get(I));
ps.setString(2,’老李老师);
ps.setDouble(3,价格。get(I));
}
@覆盖
public int getBatchSize() {
返回标题。size();//总长度
}
});
}基于对象
@测试
public void testInsertBatch2() {
List Object[] params=List.of(
新对象[]{ 春天开发实战, 11, 89.0},
新对象[]{ 春天开发实战1, 11, 89.0},
新对象[]{ 春天开发实战2, 11, 89.0},
新对象[]{ 春天开发实战3, 11, 89.0}
);
字符串SQL= insert into yootk。书(书名、作者、价格)值(?);
int[]result=JDBC模板。批量更新(SQL,params);//批量插入
系统。出去。println( result= result);
}2.3.4、查在数据库操作过程中,除了数据更新操作之外,最为繁琐的就是数据库的查询功能了。由于使用设计的定位属于或映射组件,所以就需要在查询完成之后,可以自动的将查询结果转为维多利亚皇家勋章类型的实例,而为了解决该问题,在SpringJDBC中提供了一个行映射程序接口,这个接口可以实现结果集向指定对象实例的转换。该接口提供有一个mapRow()处理方法,可以接收查询结果每行数据的结果集,用户可以将指定列取出,并保存在自标维多利亚皇家勋章实例之中
查询单个书对象根据数据库创建
@数据
@AllArgsConstructor
@NoArgsConstructor
公共类图书{
私人整数出价;
私有字符串标题;
私有字符串作者;
私人双倍价格;
}测试类://查询单个
@测试
public void testQuery() {
String sql=select bid,title,author,price from yootk.book where bid=?;
书书=JDBC模板。查询对象(SQL,新的行映射器书(){
@覆盖
公共图书映射行(ResultSet rs,int rowNum)引发SQLException {
Book book=新书();
书。设定出价(卢比。getint(1));
书。settitle(RS。getstring(2));
书。集合作者(RS。getstring(3));
书。设定价格(卢比。get double(4));
还书;
}
}, 3);//这里的3是对预处理数据的回填多个需按照顺序编写
系统。出去。println(【查询对象查询结果】book= book);
}查询多个//查询所有
@测试
public void testQueryAll() {
String sql=select bid,title,author,price from yootk。书’;
列表帐簿列表=JDBC模板。query(SQL,new RowMapper Book () {
@覆盖
公共图书映射行(ResultSet rs,int rowNum)引发SQLException {
Book book=新书();
书。设定出价(卢比。getint(1));
书。settitle(RS。getstring(2));
书。集合作者(RS。getstring(3));
书。设定价格(卢比。get double(4));
还书;
}
});
list.stream().forEach(系统。out:println);
}分页查询//分页
@测试
public void testQuerySpAll() {
int current=2;//页数
int size=5;//每页数量
String sql=select bid,title,author,price from yootk.book limit? ;
列表帐簿列表=JDBC模板。query(SQL,new RowMapper Book () {
@覆盖
公共图书映射行(ResultSet rs,int rowNum)引发SQLException {
Book book=新书();
书。设定出价(卢比。getint(1));
书。settitle(RS。getstring(2));
书。集合作者(RS。getstring(3));
书。设定价格(卢比。get double(4));
还书;
}
} 、( current - 1) * size,size);
list.stream().forEach(系统。out:println);
}统计行数//查询行数
@测试
public void testQueryCount() {
string SQL= select count(*)from yootk。书名像哪里的书?
长计数=JDBC模板。查询对象(SQL,new RowMapper Long () {
@覆盖
公共Long mapRow(ResultSet rs,int rowNum)引发SQLException {
返回RS。变长(1);
}
} 、% Spring % ";
LOGGER.info(【数据库记录总行数】{} ,计数);
}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。