当前位置: 首页 > news >正文

专业的购物网站建设seo优化培训学校

专业的购物网站建设,seo优化培训学校,上海省住房与城乡建设厅网站,怀化找什么人做网站目录标题前言一、数据库连接方式1.JDBC连接数据库2.Spring Jdbc连接数据库(JdbcTemplate)二、JdbcTemplate源码分析1.update/save功能的实现源码分析入口(关键)基础方法execute1.获取数据库连接池2.应用用户设定的输入参数3. 调用回调函数处理4. 资源释放Update中的回调函数2.q…

目录标题

  • 前言
  • 一、数据库连接方式
    • 1.JDBC连接数据库
    • 2.Spring Jdbc连接数据库(JdbcTemplate)
  • 二、JdbcTemplate源码分析
    • 1.update/save功能的实现
      • 源码分析入口(关键)
      • 基础方法execute
        • 1.获取数据库连接池
        • 2.应用用户设定的输入参数
        • 3. 调用回调函数处理
        • 4. 资源释放
      • Update中的回调函数
    • 2.query 功能的实现
      • 源码分析入口1(关键)
      • 源码分析入口2(关键)
    • 3.queryForObject
  • 总结

前言

汇总:《Spring源码深度分析》持续更新中…

本章主要以Spring提供的模板类’JdbcTemplate‘为例,进行源代码分析。如果有小伙伴对Spring如何支持持久化技术的理论知识感兴趣,可以参考《精通Spring4.x 企业应用开发实战》第10章 Spring对Dao的支持。

一、数据库连接方式

从《精通Spring4.x 企业应用开发实战》第10章 Spring对Dao的支持
文章中,我们知道目前市场上连接MySQL数据库常用的几种持久化方式有五种:JDBC、Mybatis、Hibernate、JTA、JDO。

然而Spring又分别对JDBC、Hibernate、JTA、JDO提供了模板类,以便快速进行持久化连接开发。其中Spring针对JDBC的模板类是’JdbcTemplate‘。
在这里插入图片描述

下面,我们将分别对JDBC和JdbcTemplate数据库连接进行分析。

1.JDBC连接数据库

JDBC ( Java Data Base Connectivity, Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 为数据库开发人员提供了一个标准的 API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序,并且可跨平台运行,并且不受数据库供应商的限制。
JDBC 连接数据库的流程及其原理如下。
(1)在开发环境中加载指定数据库的驱动程序。接下来的实验中,使用的数据库是 MysQL,所以需要去下载 MySQL 支持 JDBC 的驱动程序(最新的版本是 mysal-conneetorjava-5.1.18-bin.jar),将下载得到的驱动程序加载进开发环境中(开发环境是 MyEclipse,具体示例时会讲解如何加载)。

(2)在Java 程序中加载驱动程序。在 Java 程序中,可以通过“Class.forName(“指定数据库的驱动程序”)”的方式来加载添加到开发环境中的驱动程序,例如加载 MySQL 的数据驱动程序的代码为 Class. forName(“com.mysqljdbc.Driver”)。

(3)创建数据连接对象。通过 DriverManager 类创建数据库连接对象 Conneetion。DriverManager 类作用于 Java 程序和 JDBC 驱动程序之间,用于检查所加载的驱动程序是否可以建立连接,然后通过它的 getConnection 方法根据数据库的 URL、用户名和密码,创建一个JDBC Connection 对象,例如:Connection connection = DriverManager.getConnection(“连接数据库的 URL”,“用户名”,"密码”)。其中,URL-协议名+1P 地址(域名) +端口+数据库名称;用户名和密码是指登录数据库时所使用的用户名和密码。具体示例创建 MySQL 的数据库连接代码如下:

Connection connectMySQL = DriverManager.getConnection (“jdbc:mysql://localhost: 3306/myuser","root","root" );

(4)创建 Statement 对象。Statement 类的主要是用于执行静态 SQL 语句并返回它所生成结果的对象。通过 Connection 对象的 createStatement()方法可以创建一个 Statement 对象。例如:Statement statament = connection.createStatement()。具体示例创建 Statement 对象代码如下:

Statement statamentMySQL = connectMySQL.createStatement ();

(5)调用 Statement 对象的相关方法执行相对应的 SQL 语句。通过 execuUpdate()方法来对数据更新,包括插入和删除等操作,例如向 staff 表中插人一条数据的代码:statement.excuteUpdate( “INSERTINTO staff (name, age, sex,address,depart,worklen, wage) " + " VALUES (‘Tom1’, 321,“M’,‘china’,Personnel’,,3”,,3000’)”);通过调用 Statement 对象的 executeQuery()方法进行数据的查询,而查询结果会得到 ResulSet对象,ResuSet 表示执行查询数据库后返回的数据的集合,ResulSet 对象具有可以指向当前数据行的指针。通过该对象的 next()方法,使得指针指向下一行,然后将数据以列号或者字段名取出。如果当 next()方法返回 null,则表示下一行中没有数据存在。使用示例代码如下:

Resultset resultsel = statement.executeQuery( “select * from staff" );

(6)关闭数据库连接。使用完数据库或者不需要访问数据库时,通过 Connection 的 close()方法及时关闭数据连接。

2.Spring Jdbc连接数据库(JdbcTemplate)

Spring 中的 JDBC 连接与直接使用 JDBC 去连接还是有所差别的,Spring 对 JDBC 做了大量封裝,消除了冗余代码,使得开发量大大减小。下面通过一个小例子让大家简单认识 Spring中的 JDBC 操作。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

二、JdbcTemplate源码分析

1.update/save功能的实现

源码分析入口(关键)

无论对哪种技术进行源码分析,我们都应该先找到入口点,然后顺藤摸瓜式的解读源码。

我们以上面的例子为基础开始分析 Spring 中对JDBC 的支持,首先寻找整个功能的切入点,在示例中我们可以看到所有的数据库操作都封装在了 UserServicelmpl 中,而 UserServicelmpl中的所有数据库操作又以其内部属性 jdbcTemplate 为基础。这个jdbcTemplate 可以作为源码分析的切人点,我们一起看看它是如何实现又是如何被初始化的。

在 UserServicelmpl 中 jdbcTemplate 的初始化是从 setDataSource 函数开始的,DataSource实例通过参数注入,DataSource 的创建过程是引入第三方的连接池,这里不做过多介绍。DataSource 是整个数据库操作的基础,里面封装了整个数据库的连接信息。我们首先以保存实体类为例进行代码跟踪。
在这里插入图片描述
对于保存一个实体类来讲,在操作中我们只需要提供 SQL 语句以及语句中对应的参数和参数类型,其他操作便可以交由 Spring 来完成了,这些工作到底包括什么呢?进入 jdbcTemplate中的 update 方法。

	@Overridepublic int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {return update(sql, newArgTypePreparedStatementSetter(args, argTypes));}@Overridepublic int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {return update(new SimplePreparedStatementCreator(sql), pss);}

进人 update 方法后,我们发现Spring 并不是急于进入核心处理操作,而是先做足准备工作:

  1. 使用 ArgTypePreparedStatementSetter 对参数与参数类型进行封装
  2. 同时又使用 SimplePreparedStatementCreator 对 SQL 语句进行封装
    至于为什么这么封装,暂且留下悬念。

经过了数据封装后便可以进入了核心的数据处理代码了。

   protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)throws DataAccessException {logger.debug("Executing prepared SQL update");// PreparedStatementCallback作为回调函数。execute 方法是最基础的操作,而其他操作比如update、 query 等方法则是传人不同的 PreparedStatementCallback 参数来执行不同的逻辑,return updateCount(execute(psc, ps -> {try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValue: 将参数赋值给对应的参数类型pss.setValues(ps);}// ps.executeUpdate(): JDBC-API的基本操作int rows = ps.executeUpdate();if (logger.isTraceEnabled()) {logger.trace("SQL update affected " + rows + " rows");}return rows;}finally {if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}, true));}

基础方法execute

	/*** 功能描述:作为'公用的method',被具有'个性化'的方法(增删改查)调用。*         	execute 作为数据库操作的核心人口,將大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数 PreparedStatementCallback 进行回调。** @param psc* @param action* @param closeResources* @return* @param <T>* @throws DataAccessException*/@Nullableprivate <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action, boolean closeResources)throws DataAccessException {Assert.notNull(psc, "PreparedStatementCreator must not be null");Assert.notNull(action, "Callback object must not be null");if (logger.isDebugEnabled()) {String sql = getSql(psc);logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));}// 获取与'数据库事务'相绑定的'数据库连接'。Connection con = DataSourceUtils.getConnection(obtainDataSource());PreparedStatement ps = null;try {ps = psc.createPreparedStatement(con);applyStatementSettings(ps);// 执行数据库操作(CRUD)。处理一些通用方法外的个性化处理,也就是 PreparedStatementCallback 类型的参数的doInPreparedStatement 方法的回调。T result = action.doInPreparedStatement(ps);handleWarnings(ps);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}String sql = getSql(psc);psc = null;JdbcUtils.closeStatement(ps);ps = null;// 数据库的连接释放并不是直接调用了 Connection 的API中的close 方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库连接,这种情况下直接使用 ConnectionHolder 中的 released 方法进行连接数减一,面不是真正的释放连接。DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw translateException("PreparedStatementCallback", sql, ex);}finally {if (closeResources) {if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}JdbcUtils.closeStatement(ps);DataSourceUtils.releaseConnection(con, getDataSource());}}}

下面,我们对execute代码中重要的几个关键点进行分析。

1.获取数据库连接池

获取数据库连接也并非直接使用 dataSource.getConnection()方法那么简单,同样也考虑了诸多情况。

在数据库连接方面,Spring 主要考虑的是关于事务方面的处理。基于事务处理的特殊性,Spring 需要保证线程中的数据库操作都是使用同一个事务连接

public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {conHolder.requested();if (!conHolder.hasConnection()) {logger.debug("Fetching resumed JDBC Connection from DataSource");conHolder.setConnection(fetchConnection(dataSource));}return conHolder.getConnection();}// Else we either got no holder or an empty thread-bound holder here.logger.debug("Fetching JDBC Connection from DataSource");Connection con = fetchConnection(dataSource);// 功能描述:判断'当前线程'是否存在事务。// 目的:spring需要保证线程下的数据库操作,都使用同一个事务连接。那么当事务回滚的时候,可以一次性回滚所有的数据库操作。if (TransactionSynchronizationManager.isSynchronizationActive()) {try {// Use same Connection for further JDBC actions within the transaction.// Thread-bound object will get removed by synchronization at transaction completion.// 在事务中,使用同一个数据库连接。ConnectionHolder holderToUse = conHolder;if (holderToUse == null) {holderToUse = new ConnectionHolder(con);}else {holderToUse.setConnection(con);}holderToUse.requested();TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));holderToUse.setSynchronizedWithTransaction(true);if (holderToUse != conHolder) {TransactionSynchronizationManager.bindResource(dataSource, holderToUse);}}catch (RuntimeException ex) {// Unexpected exception from external delegation call -> close Connection and rethrow.releaseConnection(con, dataSource);throw ex;}}return con;}

2.应用用户设定的输入参数

	protected void applyStatementSettings(Statement stmt) throws SQLException {int fetchSize = getFetchSize();if (fetchSize != -1) {stmt.setFetchSize(fetchSize);}int maxRows = getMaxRows();if (maxRows != -1) {stmt.setMaxRows(maxRows);}DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());}

a: setFetchsize 最主要是为了减少网络交互次数设计的。访问 ResultSet 时,如果它每次只从服务器上读取一行数据,则会产生大量的开销。setFetchSize 的意思是当调用 rs.next 时,ResultSet会一次性从服务器上取得多少行数据回来,这样在下次 rs.next 时,它可以直接从内存中获取数据而不需要网络交互,提高了效率。这个设置可能会被某些 JDBC 驱动忽略,而且设置过大也会造成内存的上升。

b: setMaxRows 将此 Statement对象生成的所有 ResulSet 对象可以包含的最大行数限制设置为给定数。

3. 调用回调函数处理

一些通用方法外的个性化处理,也就是 PreparedStatementCallback 类型的参数的dolnPreparedStatement 方法的回调。

4. 资源释放

数据库的连接释放并不是直接调用了 Connection 的APL中的.close 方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库连接,这种情况下直接使用 ConnectionHolder 中的 released 方法进行连接数减一,面不是真正的释放连接。

public static void 	doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {if (con == null) {return;}if (dataSource != null) {// 当前线程存在事务的情况下说明存在'共用数据库连接',直接使用 ConnectionHolder 中的released 方法进行连接数减一而不是真正的释放连接。ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && connectionEquals(conHolder, con)) {// It's the transactional Connection: Don't close it.conHolder.released();return;}}// 直接使用Connection的API,调用close方法释放连接doCloseConnection(con, dataSource);}

Update中的回调函数

PreparedStatementCalback 作为一个接口,其中只有一个函数 doInPreparedStatement,这个函数是用于调用通用方法 execute 的时候无法处理的一些个性化处理方法,在 update 中的函数实现:

   protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)throws DataAccessException {logger.debug("Executing prepared SQL update");// PreparedStatementCallback作为回调函数。execute 方法是最基础的操作,而其他操作比如update、 query 等方法则是传人不同的 PreparedStatementCallback 参数来执行不同的逻辑,return updateCount(execute(psc, ps -> {try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValue: 将参数赋值给对应的参数类型pss.setValues(ps);}// ps.executeUpdate(): JDBC-API的基本操作int rows = ps.executeUpdate();if (logger.isTraceEnabled()) {logger.trace("SQL update affected " + rows + " rows");}return rows;}finally {if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}, true));}

其中用于真正执行 SQL 的 ps.executeUpdate 没有太多需要讲解的,因为我们平时在直接使用JDBC 方式进行调用的时候会经常使用此方法。但是,对于设置输人参数的函数 pss.set Values(ps),我们有必要去深人研究一下。在没有分析源码之前,我们至少可以知道其功能,不妨再回顾下 Spring 中使用 SOL 的执行过程,直接使用:
在这里插入图片描述
SQL 语句对应的参数,对应参数的类型清晰明了,这都归功于 Spring 为我们做了封裝,而真正的 JDBC 调用其实非常繁琐,你需要这么做:
在这里插入图片描述那么看看 Spring 是如何做到封装上面的操作呢?首先,所有的操作都是以 pss.setValues(ps)为入口的。还记得我们之前的分析路程吗?这个pss 所代表的当前类正是 ArgPreparedStatementSetter。其中的 setValues 的相关源码分析此处略。

2.query 功能的实现

源码分析入口1(关键)

在之前的章节中我们介绍了 update 方法的功能实现,那么在数据库操作中查找操作也是使用率非常高的函数,同样我们也需要了解它的实现过程。使用方法如下:
在这里插入图片描述

	@Overridepublic <T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException {return result(query(sql, args, argTypes, new RowMapperResultSetExtractor<>(rowMapper)));}@Override@Nullablepublic <T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse) throws DataAccessException {// 与 update 方法中都同样使用了 newArgTypePreparedStatementSetter。return query(sql, newArgTypePreparedStatementSetter(args, argTypes), rse);}@Override@Nullablepublic <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {return query(new SimplePreparedStatementCreator(sql), pss, rse);}

核心代码如下:

	@Nullablepublic <T> T query(PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)throws DataAccessException {Assert.notNull(rse, "ResultSetExtractor must not be null");logger.debug("Executing prepared SQL query");// 此处会调用和update一样的execute方法return execute(psc, new PreparedStatementCallback<T>() {@Override@Nullablepublic T doInPreparedStatement(PreparedStatement ps) throws SQLException {ResultSet rs = null;try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValuepss.setValues(ps);}// ps.executeQuery():JDBC-API的基本操作rs = ps.executeQuery();// rse.extractData(rsToUse)方法负责将结果进行封装并转换至 POJO, rse 当前代表的类为RowMapperResultSetExtractor,而在构造 RowMapperResultSetExtractor 的时候我们又将自定义的 rowMapper 设置了进去。return rse.extractData(rs);}finally {JdbcUtils.closeResultSet(rs);if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}}, true);}

可以看到整体套路与 update 差不多的,只不过在回调类 PreparedStatementCallback 的:中使用的是 ps.executeQuery()执行查询操作,而且在返回方法上也做了一些额外的处理。

源码分析入口2(关键)

之前讲了 update 方法以及 query 方法,使用这两个附数示例的 SQL 都是带有参数的,也就是带有“?”的,那么还有另一种情况是不带有“?”的,Spring 中使用的是另一种处理方式。例如:

List<user> 1ist = jdbcTemplate.query ("selectfrom user", new UserRowMapper() );

追踪进入:

	@Overridepublic <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));}

核心源码:

    /*** 功能描述:与之前的 query 方法最大的不同是少了参数及参数类型的传递,自然也少了 PreparedStatementSetter 类型的封装。* * @param sql the SQL query to execute* @param rse a callback that will extract all rows of results*/@Override@Nullablepublic <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {Assert.notNull(sql, "SQL must not be null");Assert.notNull(rse, "ResultSetExtractor must not be null");if (logger.isDebugEnabled()) {logger.debug("Executing SQL query [" + sql + "]");}/*** Callback to execute the query.*/class QueryStatementCallback implements StatementCallback<T>, SqlProvider {@Override@Nullablepublic T doInStatement(Statement stmt) throws SQLException {ResultSet rs = null;try {rs = stmt.executeQuery(sql);return rse.extractData(rs);}finally {JdbcUtils.closeResultSet(rs);}}@Overridepublic String getSql() {return sql;}}// 既然少了 PreparedStatementSetter 类型的传入,调用的 execute 方法自然也会有所改变了。return execute(new QueryStatementCallback(), true);}

execute方法:

    /*** 功能描述:这个 exexute 与之前的 execute 并无太大差别,都是做一些常规的处理,诸如获取连接、释连接等。*         但是,有一个地方是不一样的,就是 statement 的创建。这里直接使用connection 创建,而带有参数的 SQL 使用的是 PreparedStatementCreator 类来创建的。一个是普通的 Statement,另一个是 PreparedStatement。** @param <T>* @throws DataAccessException*/@Nullableprivate <T> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = DataSourceUtils.getConnection(obtainDataSource());Statement stmt = null;try {stmt = con.createStatement();applyStatementSettings(stmt);T result = action.doInStatement(stmt);handleWarnings(stmt);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.String sql = getSql(action);JdbcUtils.closeStatement(stmt);stmt = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw translateException("StatementCallback", sql, ex);}finally {if (closeResources) {JdbcUtils.closeStatement(stmt);DataSourceUtils.releaseConnection(con, getDataSource());}}}

PreparedStatement 接口继承 Statement,并与之在两方面有所不同:

  1. PreparedStatement 实例包含已编译的 SQL 语句.这就是使语向“准备好”。包含于PreparedStatement 对象中的 SQL 语句可具有一个或多个IN 参数。IN 参数的值在 SQL语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“2”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的 setXXX 方法来提供。
  2. 由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

3.queryForObject

Spring 中不仅仅为我们提供了 query 方法,还在此基础上做了封装,提供了不同类型的 query方法。此处源码分析略。

总结

待补充。

http://www.ds6.com.cn/news/3265.html

相关文章:

  • 诸城易讯网站建设服务中心免费建站建站abc网站
  • 怎么根据已有网站做新网站环球军事网最新消息
  • 网站建设设计维片中国万网域名查询
  • 学生组织网站建设四川seo推广方案
  • 网站制作建设上海知名网站制作公司
  • 中国建设网站的公司公司网站页面设计
  • 专注于网络推广及网站建设搜索引擎排名2020
  • 金坛建设银行总行网站企业营销策划书范文
  • 桂林wordpress招聘推广seo优化公司
  • 网站建设关于十大暗网搜索引擎
  • 吉林网站建设电话进入百度首页
  • 用php做网站需要什么百度指数搜索榜度指数
  • 这样做的网站网站性能优化的方法有哪些
  • 苏州做网站的下载百度官方网站
  • html 模板网站免费创建网站平台
  • 盐城网站建设哪家好快速建站哪个平台好
  • wordpress 禁止右键凌云seo博客
  • wordpress后台添加图片seo广告优化多少钱
  • 做网站需要哪些技术支持怎么做表格
  • 重庆做网站建设的公司百度seo怎么做
  • 网页制作模板中文seo的课谁讲的好
  • 网站空间一般多大情感营销
  • 网站备案个人信息泄露怎么seo快速排名
  • 网站建设入驻百度网盘怎么找资源
  • 哪个网站反盗版做的最好海南乐秀同城群软件下载
  • 哪个网站能帮助做试卷公司网站首页设计
  • wordpress实现mp4播放上海网站建设seo
  • 展示型网站与营销型网站著名的网络营销案例
  • 网站的优化是什么意思腾讯云域名注册官网
  • 网站开发的背景意义软件开发公司排名