尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Jdbc不香吗?聊聊spring jdbcTemplate吧?

Jdbc不香吗?聊聊spring jdbcTemplate吧? 背景小白刚入公司时领导指定小白的师傅为扫地僧。在介绍公司内部框架时小白提出了自己的疑问小白师傅数据库操作JDBC用着挺好的为什么非要使用mybatishibernateSpring jdbcTemplate这些框架这样会不会导致新手入门门槛变高扫地僧如果你想要变成一个整天忙的脚不沾地四处救火的程序员那你可以选择直接使用JDBC如果你只想专注于业务开发,那么像Mybatis这些框架可以帮我们把脏活累活帮我们处理好.事实胜于雄辩。下面我来演示一个使用jdbc访问mysql数据库的示例你来看看如果变成线上的项目有什么地方值得改进Jdbc不香吗准备工作mysql数据库,本实例的版本为:8.0.16mysql客户端SQLyog(免费不需要注册码)或者navicat for mysql创建数据库www和表CREATEdatabase davidwang456;use davidwang456;DROPTABLEIFEXISTSstudent;CREATETABLEstudent(idint(11)NOTNULLAUTO_INCREMENT,first_namevarchar(100)DEFAULTNULL,last_namevarchar(100)DEFAULTNULL,ageint(11)DEFAULTNULL,PRIMARYKEY(id))ENGINEInnoDBDEFAULTCHARSETutf8mb4;mock数据INSERTINTOstudentVALUES(1,wang1,david1,25);INSERTINTOstudentVALUES(2,wang2,david2,25);INSERTINTOstudentVALUES(3,wang3,david3,25);INSERTINTOstudentVALUES(4,wang4,david4,25);INSERTINTOstudentVALUES(5,wang5,david5,25);INSERTINTOstudentVALUES(6,wang6,david6,25);INSERTINTOstudentVALUES(7,wang7,david7,25);INSERTINTOstudentVALUES(8,wang8,david8,25);创建maven项目添加依赖完整依赖文件pom.xml(包含了后面用到的spring jdbcTemplate依赖相关)project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.davidwang45.mybatis/groupId artifactIdJdbcTest/artifactId properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties version1.2.0-SNAPSHOT/version dependencies dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.6/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.16/version scopeprovided/scope /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.16/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-core/artifactId version5.2.9.RELEASE/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-beans/artifactId version5.2.9.RELEASE/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.2.9.RELEASE/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version5.2.9.RELEASE/version /dependency /dependencies /project测试程序使用JDBC操作数据库的基本步骤大致相同加载注册数据库驱动到JVM。建立获取数据库连接。定义操作的SQL语句,实例化PreparedStatement对象,设置入参。执行数据库操作。获取并操作结果集。关闭对象回收数据库资源关闭结果集–关闭数据库操作对象–关闭连接importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.Statement;publicclassJdbcTest{// MySQL 8.0 以下版本 - JDBC 驱动名及数据库 URL//static final String JDBC_DRIVER com.mysql.jdbc.Driver;// static final String DB_URL jdbc:mysql://localhost:3306/davidwang456;// MySQL 8.0 以上版本 - JDBC 驱动名及数据库 URLstaticfinalStringJDBC_DRIVERcom.mysql.cj.jdbc.Driver;staticfinalStringDB_URLjdbc:mysql://localhost:3306/davidwang456?characterEncodingUTF-8useSSLfalseuseLegacyDatetimeCodefalseserverTimezoneUTC;// 数据库的用户名与密码需要根据自己的设置staticfinalStringUSERroot;staticfinalStringPASSwangwei456;publicstaticvoidmain(String[]args){Connectionconnnull;PreparedStatementpreparedStatementnull;ResultSetrsnull;Stringsql;try{//1 注册 JDBC 驱动Class.forName(JDBC_DRIVER);//2 打开链接System.out.println(连接数据库...);connDriverManager.getConnection(DB_URL,USER,PASS);//3 定义操作的SQL语句,实例化PreparedStatement对象,设置入参sqlSELECT id, first_name, last_name,age FROM student where id ?;System.out.println( 实例化PreparedStatement对象...);preparedStatementconn.prepareStatement(sql);preparedStatement.setInt(1,5);//4 执行数据库操作rspreparedStatement.executeQuery();//5 获取并操作结果集while(rs.next()){// 通过字段检索intidrs.getInt(id);Stringfirst_namers.getString(first_name);Stringlast_namers.getString(last_name);intagers.getInt(age);//输出数据System.out.println([ID: id,first_name:first_name,last_name:last_name,age:age]);}//6 完成后关闭// 关闭资源shutdownResource(conn,preparedStatement,rs);}catch(SQLExceptionse){// 处理 JDBC 错误se.printStackTrace();}catch(Exceptione){// 处理 Class.forName 错误e.printStackTrace();}finally{shutdownResource(conn,preparedStatement,rs);}}publicstaticvoidshutdownResource(Connectionconn,Statementstmt,ResultSetrs){// 关闭资源try{if(rs!null){rs.close();}}catch(SQLExceptionse1){//TODO}try{if(stmt!null)stmt.close();}catch(SQLExceptionse2){//TODO}try{if(conn!null)conn.close();}catch(SQLExceptionse){//TODO}}}运行结果连接数据库...实例化PreparedStatement对象...[ID:5,first_name:wang5,last_name:david5,age:25]获取结果符合预期。小白线上系统使用jdbc时因为使用jdbc的地方非常多资源的释放很容易忘记导致数据库资源不能释放浪费数据库的性能。扫地僧确实如此当我们注释掉上面的释放资源代码时可以根据一些sql命令来查询mysql的连接,在navica客 户端上执行查看连接线程建立连接情况数据如下show status like Threads%; ​ variable_name value ​ Threads_cached 1 ​ Threads_connected 4 ​ Threads_created 5 ​ Threads_running 2查看具体的连接线程SHOW PROCESSLIST; ​ ID,USER,HOST,DB,COMMAND,TIME,STATE,INFO ​ 4 event_scheduler localhost Daemon 7230 Waiting on empty queue ​ 22 root localhost:6717 Sleep 1864 ​ 23 root localhost:6719 davidwang456 Sleep 1863 ​ 24 root localhost:6720 davidwang456 Sleep 1857 ​ 25 root localhost:6721 davidwang456 Query 0 starting SHOW PROCESSLIST杀掉连接数使用kill命令​ 示例kill 23扫地僧还会其它的问题吗小白分页时使用JDBC会碰到要根据数据库的类型写不同的sql如mysql使用limitoracle使用rownum进行分页,即不同类型数据库要写不同的实现。扫地僧除了上面的问题还有1. sql会分散到项目中的不同地方难以管理 2. 动态sql语句生产容易产生sql注入问题 3. 如果想要高性能需要自己实现一级甚至二级缓存4. 代码量会增大从而导致出bug的概率增大5. 事务管理也需要开发者操心等等问题这就导致了一些ORM框架的产生比较流行的就有HibernateMybatisJPASpring Jdbc。现在来看jdbc还香吗小白我听说最早流行的开发框架是S(Spring)S(Struts)H(Hibernate),后面流行S(Spring)S(Spring mvc)M(Mybatis),现在流行Spring boot和Spring Cloud微服务甚至Service Mesh等框架了。那我们为什么选择Mybatis而不是Spring 自带的Spring jdbc呢这样变成S(Spring)S(Spring mvc)S(Spring jdbc)全家桶岂不是更好扫地僧好那我们今天先讨论一下Spring Jdbc的优缺点吧MybatisHibernatejpa留待我们以后讨论。Spring JdbcTemplateJdbcTemplate采用了模板方法的设计思想和RowMapper配合简化了jdbc访问。看下面的示例接着上面的项目添加依赖注意上面pom.xml对spring依赖dependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactIdversion5.2.9.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactIdversion5.2.9.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.2.9.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion5.2.9.RELEASE/version/dependency配置JdbcTemplate?xml version 1.0 encoding UTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd!-- Initialization for data source --beaniddataSourceclassorg.springframework.jdbc.datasource.DriverManagerDataSourcepropertynamedriverClassNamevaluecom.mysql.cj.jdbc.Driver/propertynameurlvaluejdbc:mysql://localhost:3306/davidwang456?characterEncodingUTF-8useSSLfalseuseLegacyDatetimeCodefalseserverTimezoneUTC/propertynameusernamevalueroot/propertynamepasswordvaluewangwei456//bean!-- Definition for studentJDBCTemplate bean --beanidjdbcTemplateclassorg.springframework.jdbc.core.JdbcTemplatepropertynamedataSourcerefdataSource//bean/beans测试程序实体类增加了create方法importjava.io.Serializable;importlombok.Data;DatapublicclassStudentDTOimplementsSerializable{privatestaticfinallongserialVersionUID1L;privateIntegerid;privateStringfirst_name;privateStringlast_name;privateIntegerage;OverridepublicStringtoString(){returnthis.id,this.first_name,this.last_name,age;}publicstaticStudentDTOcreate(Stringfirst_name,Stringlast_name,Integerage){StudentDTOdtonewStudentDTO();dto.setAge(age);dto.setFirst_name(first_name);dto.setLast_name(last_name);returndto;}}测试类importjava.sql.ResultSet;importjava.sql.SQLException;importjava.util.Arrays;importjava.util.List;importjavax.sql.DataSource;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;importorg.springframework.jdbc.core.RowMapper;importorg.springframework.jdbc.core.namedparam.MapSqlParameterSource;importorg.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;publicclassJdbcTemplateTest{publicstaticvoidmain(String[]args){queryBatch();}publicstaticvoidqueryBatch(){ListIntegeridsArrays.asList(5,6,8);MapSqlParameterSourceparametersnewMapSqlParameterSource();parameters.addValue(ids,ids);ApplicationContextcontextnewClassPathXmlApplicationContext(application.xml);DataSourcedataSource(DataSource)context.getBean(dataSource);NamedParameterJdbcTemplatejdbcTemplatenewNamedParameterJdbcTemplate(dataSource);ListStudentDTOlistjdbcTemplate.query(SELECT id,first_name,last_name,age FROM student WHERE id IN (:ids),parameters,newRowMapperStudentDTO(){OverridepublicStudentDTOmapRow(ResultSetresultSet,inti)throwsSQLException{StudentDTOstuStudentDTO.create(resultSet.getString(first_name),resultSet.getString(last_name),resultSet.getInt(age));stu.setId(resultSet.getInt(id));returnstu;}});list.stream().forEach(System.out::println);}}运行结果如下5,wang5,david5,256,wang6,david6,258,wang8,david8,25从上面的示例可以看到JdbcTemplate解决了单纯使用JDBC而出现的大部分问题资源的获取与释放支持不同类型的数据库分页也可以通过spring-data的Pageable与Page实现。事务管理也不需要开发者操心代码量会减少同时减少bug的概率但同时存在如下问题sql会分散到项目中的不同地方难以管理 动态sql语句生产容易产生sql注入问题如果想要高性能需要自己实现一级甚至二级缓存和Spring深度耦合必须于Spring框架结合在一起使用。而MybatisHibernateJPA都能解决上面的问题故我们的选择不是Spring Jdbc。总结扫地僧通过上面两个简单的示例我们可以看出单纯使用jdbc和spring jdbcTemplate对复杂的生产环境来说并不适用。特别重要的两点是sql会分散到项目中的不同地方难以管理如果想要高性能需要自己实现一级甚至二级缓存它们对项目的管理和性能尤为重要。小白是不是意味着如果是演示类的项目或者非常简单的项目则可以使用jdbc或者Spring jdbcTemplate例如使用jdbc时可以将jdbc的公共代码抽取出来组成一个JdbcUtil工具类将执行的sql都封装成常量放到同一个文件(静态sql)通过调用jdbcUtil来执行可以避免很多问题。扫地僧是的对于一些临时项目或者短期项目为了快速实现可以选择jdbc来做。扫地僧最后一点也是我最想告诉 你的是开发时总会觉得规范这种东西离我们很远但这些规范恰恰从根本上解决了我们的绝大部分问题熟悉这些规范可以让我们在开发的路上走的更远效率更高。JDBC规范的最新版本为JDBC 4.3 Specification,地址为https://jcp.org/aboutJava/communityprocess/mrel/jsr221/index3.html如果想要走的更远请下载下来一定要仔细研读一番它.延伸阅读与资源Java 工程师进阶从 JVM 生产排障到OpenJDK原理NumPy 从入门到生产落地全链路实战指南科学计算/向量化Redis 8 实战精讲从 CRUD 到源码构建高可用缓存系统Redis 实战修炼与原理进阶Python 3实战精进从脚本到高并发订单引擎python入门Rquests从菜鸟脚本到企业级SDK的网络实战圣经Milvus向量数据库实战修炼从 0 到 1精通向量检索与生产落地MongoDB 实战进阶与内核修炼后端工程师的 AI 转型第一课Ollama 与私有化大模型实战10倍开发者的 Dify 魔法书从零构建全栈 AI 应用后端工程师转型AI第一课-Ollama 与私有化大模型实战大型语言模型(LLM) vLLM 高性能推理落地实战Agent开发之LlamaIndex 实战修炼与源码进阶大语言模型Transformers 实战修炼与源码剖析
返回列表