目录导航

  • 前言
  • SpringJDBC
    • 异常处理
    • config 模块
    • core 模块
      • JdbcTeamplate
      • RowMapper
      • 元数据 metaData 模块
      • 使用 SqlParameterSource 提供参数值
      • simple 实现
    • DataSource
    • object 模块
    • JdbcTemplate是 core包的核心类
  • 开发ORM框架
    • 为什么要自己写ORM框架?
    • 实际场景应用
    • 实现ORM操作
    • 手写ORM框架升级
    • 疑问解析
  • 后记

前言

使用 Spring 进行基本的 JDBC 访问数据库有多种选择。Spring 至少提供了三种不同的工作模式: JdbcTemplate, 一个在 Spring2.5 中新提供的 SimpleJdbc 类能够更好的处理数据库元数据; 还有一种称之为 RDBMS Object 的风格的面向对象封装方式, 有点类似于 JDO 的查询设计。 我们在这里简要列举你采取某一种工作方式的主要理由. 不过请注意, 即使你选择了其中的一种工作模式, 你依然可以在你的代码中混用其他任何一种模式以获取其带来的好处和优势。 所有的工作模式都必须要求 JDBC 2.0 以上的数据库驱动的支持, 其中一些高级的功能可能需要 JDBC 3.0 以上的数据库驱动支持。

JdbcTemplate - 这是经典的也是最常用的 Spring 对于 JDBC 访问的方案。这也是最低级别的封装, 其他的工作模式事实上在底层使用了 JdbcTemplate 作为其底层的实现基础。JdbcTemplate 在 JDK 1.4 以上的环境上工作得很好。

NamedParameterJdbcTemplate - 对 JdbcTemplate 做了封装,提供了更加便捷的基于命名参数的使用方式而不是传统的 JDBC 所使用的“?”作为参数的占位符。这种方式在你需要为某个 SQL 指定许多个参数时,显得更加直观而易用。该特性必须工作在 JDK 1.4 以上。

SimpleJdbcTemplate - 这个类结合了 JdbcTemplate 和 NamedParameterJdbcTemplate 的最常用的功能,同时它也利用了一些 Java 5 的特性所带来的优势,例如泛型、varargs 和 autoboxing 等,从而提供了更加简便的 API 访问方式。需要工作在 Java 5 以上的环境中。

SimpleJdbcInsert 和 SimpleJdbcCall - 这两个类可以充分利用数据库元数据的特性来简化配置。

通过使用这两个类进行编程,你可以仅仅提供数据库表名或者存储过程的名称以及一个 Map 作为参数。

其中 Map 的 key 需要与数据库表中的字段保持一致。这两个类通常和 SimpleJdbcTemplate 配合使用。

这两个类需要工作在 JDK 5 以上,同时数据库需要提供足够的元数据信息。

RDBMS 对象包括 MappingSqlQuery, SqlUpdate and StoredProcedure - 这种方式允许你在初始

化你的数据访问层时创建可重用并且线程安全的对象。该对象在你定义了你的查询语句,声明查询参数并编译相应的 Query 之后被模型化。一旦模型化完成,任何执行函数就可以传入不同的参数对之进行多次调用。这种方式需要工作在 JDK 1.4 以上。

SpringJDBC

异常处理

异常结构如下:

SQLExceptionTranslator 是 一 个 接 口 , 如 果 你 需 要 在 SQLException 和 org.springframework.dao.DataAccessException 之间作转换,那么必须实现该接口。 转换器类的实现可以采用一般通用的做法(比如使用 JDBC 的 SQLState code),如果为了使转换更准确,也可以进行定制(比如使用 Oracle 的 error code)。

SQLErrorCodeSQLExceptionTranslator 是 SQLExceptionTranslator 的默认实现。 该实现使用指定数据库厂商的 error code,比采用 SQLState 更精确。转换过程基于一个 JavaBean(类型为SQLErrorCodes)中的 error code。 这个 JavaBean 由 SQLErrorCodesFactory 工厂类创建,其中
的内容来自于 “sql-error-codes.xml”配置文件。该文件中的数据库厂商代码基于 Database MetaData 信息中的 DatabaseProductName,从而配合当前数据库的使用。

SQLErrorCodeSQLExceptionTranslator 使用以下的匹配规则:

首先检查是否存在完成定制转换的子类实现。通常 SQLErrorCodeSQLExceptionTranslator 这个类可以作为一个具体类使用,不需要进行定制,那么这个规则将不适用。

接着将 SQLException 的 error code 与错误代码集中的 error code 进行匹配。 默认情况下错误代码集将从 SQLErrorCodesFactory 取得。 错误代码集来自 classpath 下的 sql-error-codes.xml 文件,它们将与数据库 metadata 信息中的 database name 进行映射。

使用 fallback 翻译器。SQLStateSQLExceptionTranslator 类是缺省的 fallback 翻译器。

config 模块

NamespaceHandler 接口,DefaultBeanDefinitionDocumentReader 使用该接口来处理在 spring xml 配置文件中自定义的命名空间。

在jdbc 模块,我们使用 JdbcNamespaceHandler 来处理 jdbc 配置的命名空间,其代码如下:·

public class JdbcNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("embedded-database", new EmbeddedDatabaseBeanDefinitionParser()); registerBeanDefinitionParser("initialize-database", new InitializeDatabaseBeanDefinitionParser());}
}

其中,EmbeddedDatabaseBeanDefinitionParser 继承了 AbstractBeanDefinitionParser,解析 < embedded-database>元素,并使用 EmbeddedDatabaseFactoryBean 创建一个 BeanDefinition。顺便介绍一下用到的软件包 org.w3c.dom。

软件包 org.w3c.dom:为文档对象模型 (DOM) 提供接口,该模型是 Java API for XML Processing 的组件 API。该 Document Object Model Level 2 Core API 允许程序动态访问和更新文档的内容和结构。

Attr:Attr 接口表示 Element 对象中的属性。

CDATASection: CDATA 节用于转义文本块,该文本块包含的字符如果不转义则会被视为标记。

CharacterData: CharacterData 接口使用属性集合和用于访问 DOM 中字符数据的方法扩展节点。

Comment: 此接口继承自 CharacterData 表示注释的内容,即起始 ‘’ 之间的所有字符。

Document: Document 接口表示整个 HTML 或 XML 文档。

DocumentFragment: DocumentFragment 是“轻量级”或“最小”Document 对象。

DocumentType : 每个 Document 都有 doctype 属性,该属性的值可以为 null ,也可以为DocumentType 对象。

DOMConfiguration: 该 DOMConfiguration 接口表示文档的配置,并维护一个可识别的参数表。

DOMError: DOMError 是一个描述错误的接口。

DOMErrorHandler: DOMErrorHandler 是在报告处理 XML 数据时发生的错误或在进行某些其他处理(如验证文档)时 DOM 实现可以调用的回调接口。

DOMImplementation: DOMImplementation 接口为执行独立于文档对象模型的任何特定实例的操作提供了许多方法。

DOMImplementationList: DOMImplementationList 接口提供对 DOM 实现的有序集合的抽象,没有定义或约束如何实现此集合。

DOMImplementationSource:此接口允许 DOM 实现程序根据请求的功能和版本提供一个或多个实现,如下所述。

DOMLocator: DOMLocator 是一个描述位置(如发生错误的位置)的接口。

DOMStringList: DOMStringList 接口提供对 DOMString 值的有序集合的抽象,没有定义或约束此集合是如何实现的。
Element: Element 接口表示 HTML 或 XML 文档中的一个元素。

Entity: 此接口表示在 XML 文档中解析和未解析的已知实体。

EntityReference: EntityReference 节点可以用来在树中表示实体引用。

NamedNodeMap: 实现 NamedNodeMap 接口的对象用于表示可以通过名称访问的节点的集合。 NameList NameList 接口提供对并行的名称和名称空间值对(可以为 null 值)的有序集合的抽象,无需定义或约束如何实现此集合。

Node: 该 Node 接口是整个文档对象模型的主要数据类型。

NodeList: NodeList 接口提供对节点的有序集合的抽象,没有定义或约束如何实现此集合。

Notation: 此接口表示在 DTD 中声明的表示法。

ProcessingInstruction: ProcessingInstruction 接口表示“处理指令”,该指令作为一种在文档的文本中保持特定于处理器的信息的方法在 XML 中使用。

Text: 该 Text 接口继承自 CharacterData,并且表示 Element 或 Attr 的文本内容(在 XML 中称为 字符数据)。

TypeInfo: TypeInfo 接口表示从 Element 或 Attr 节点引用的类型,用与文档相关的模式指定。

UserDataHandler: 当使用 Node.setUserData() 将一个对象与节点上的键相关联时,当克隆、导入或重命名该对象关联的节点时应用程序可以提供调用的处理程序。

core 模块

JdbcTeamplate

RowMapper

元数据 metaData 模块

本节中 spring 应用到工厂模式,结合代码可以更具体了解。

CallMetaDataProviderFactory 创建 CallMetaDataProvider 的工厂类,其代码如下:

public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList( "Apache Derby","DB2","MySQL","Microsoft SQL Server","Oracle","PostgreSQL","Sybase");
/** List of supported database products for function calls */public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList( "MySQL",
"Microsoft SQL Server","Oracle","PostgreSQL");static public CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContext context) {
try {CallMetaDataProvider result = (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName()); boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData(); if (context.isFunction()) {if (!supportedDatabaseProductsForFunctions.contains(databaseProductName)) { if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for function calls"+"-- supported are: " + supportedDatabaseProductsForFunctions);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly"); accessProcedureColumnMetaData = false;
}}}else {if (!supportedDatabaseProductsForProcedures.contains(databaseProductName)) { if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for procedurecalls " +"-- supported are: " + supportedDatabaseProductsForProcedures);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly"); accessProcedureColumnMetaData = false;
}}}CallMetaDataProvider provider;if ("Oracle".equals(databaseProductName)) {provider = new OracleCallMetaDataProvider(databaseMetaData);
}else if ("DB2".equals(databaseProductName)) {provider = new Db2CallMetaDataProvider((databaseMetaData));}else if ("Apache Derby".equals(databaseProductName)) {provider = new DerbyCallMetaDataProvider((databaseMetaData));}else if ("PostgreSQL".equals(databaseProductName)) {provider = new PostgresCallMetaDataProvider((databaseMetaData));}else if ("Sybase".equals(databaseProductName)) {provider = new SybaseCallMetaDataProvider((databaseMetaData));}else if ("Microsoft SQL Server".equals(databaseProductName)) { provider = new SqlServerCallMetaDataProvider((databaseMetaData));
}else if ("HDB".equals(databaseProductName)) {provider = new HanaCallMetaDataProvider((databaseMetaData));}else {provider = new GenericCallMetaDataProvider(databaseMetaData);}if (logger.isDebugEnabled()) {logger.debug("Using " + provider.getClass().getName());}provider.initializeWithMetaData(databaseMetaData);if (accessProcedureColumnMetaData) {provider.initializeWithProcedureColumnMetaData(databaseMetaData,context.getCatalogName(), context.getSchemaName(), context.getProcedureName());}return provider;});return result;}catch (MetaDataAccessException ex) {throw new DataAccessResourceFailureException("Error retrieving database metadata", ex);}}

TableMetaDataProviderFactory 创建 TableMetaDataProvider 工厂类,其创建过程如下:

static public CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContextcontext) {try {CallMetaDataProvider result = (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName()); boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData(); if (context.isFunction()) {if (!supportedDatabaseProductsForFunctions.contains(databaseProductName)) { if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for function calls" +"-- supported are: " + supportedDatabaseProductsForFunctions);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly"); accessProcedureColumnMetaData = false;
}}}else {if (!supportedDatabaseProductsForProcedures.contains(databaseProductName)) { if (logger.isWarnEnabled()) {
logger.warn(databaseProductName + " is not one of the databases fully supported for procedurecalls " +"-- supported are: " + supportedDatabaseProductsForProcedures);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly"); accessProcedureColumnMetaData = false;
}}}CallMetaDataProvider provider;if ("Oracle".equals(databaseProductName)) {provider = new OracleCallMetaDataProvider(databaseMetaData);}else if ("DB2".equals(databaseProductName)) {provider = new Db2CallMetaDataProvider((databaseMetaData));}else if ("Apache Derby".equals(databaseProductName)) {provider = new DerbyCallMetaDataProvider((databaseMetaData));}else if ("PostgreSQL".equals(databaseProductName)) {provider = new PostgresCallMetaDataProvider((databaseMetaData));}else if ("Sybase".equals(databaseProductName)) {provider = new SybaseCallMetaDataProvider((databaseMetaData));}else if ("Microsoft SQL Server".equals(databaseProductName)) { provider = new SqlServerCallMetaDataProvider((databaseMetaData));
}else if ("HDB".equals(databaseProductName)) {provider = new HanaCallMetaDataProvider((databaseMetaData));}else {provider = new GenericCallMetaDataProvider(databaseMetaData);}if (logger.isDebugEnabled()) {logger.debug("Using " + provider.getClass().getName());}provider.initializeWithMetaData(databaseMetaData);if (accessProcedureColumnMetaData) {provider.initializeWithProcedureColumnMetaData(databaseMetaData,context.getCatalogName(), context.getSchemaName(), context.getProcedureName());}return provider;});return result;}catch (MetaDataAccessException ex) {throw new DataAccessResourceFailureException("Error retrieving database metadata", ex);}}

使用 SqlParameterSource 提供参数值

使用 Map 来指定参数值有时候工作得非常好,但是这并不是最简单的使用方式。Spring 提供了一些其他 的 SqlParameterSource 实 现 类 来 指 定 参 数 值 。 我 们 首 先 可 以 看 看

BeanPropertySqlParameterSource 类,这是一个非常简便的指定参数的实现类,只要你有一个符合 JavaBean 规范的类就行了。它将使用其中的 getter 方法来获取参数值。

SqlParameter 封 装 了 定 义 sql 参 数 的 对 象 。 CallableStateMentCallback , PrePareStateMentCallback , StateMentCallback , ConnectionCallback 回 调 类 分 别 对 应 JdbcTemplate 中的不同处理方法。

simple 实现

DataSource

spring 通过 DataSource 获取数据库的连接。Datasource 是 jdbc 规范的一部分,它通过 ConnectionFactory 获取。一个容器和框架可以在应用代码层中隐藏连接池和事务管理。

当使用 spring 的 jdbc 层,你可以通过 JNDI 来获取 DataSource,也可以通过你自己配置的第三方连接池实现来获取。流行的第三方实现由 apache Jakarta Commons dbcp 和 c3p0.


TransactionAwareDataSourceProxy 作为目标 DataSource 的一个代理, 在对目标 DataSource 包装的同时,还增加了 Spring 的事务管理能力, 在这一点上,这个类的功能非常像 J2EE 服务器所提供的事务化的 JNDI DataSource。

Note

该类几乎很少被用到,除非现有代码在被调用的时候需要一个标准的 JDBC DataSource 接口实现作为参数。 这种情况下,这个类可以使现有代码参与 Spring 的事务管理。通常最好的做法是使用更高层的抽象 来对数据源进行管理,比如 JdbcTemplate 和 DataSourceUtils 等等。

注意:DriverManagerDataSource 仅限于测试使用,因为它没有提供池的功能,这会导致在多个请求获取连接时性能很差。

object 模块

JdbcTemplate是 core包的核心类

它替我们完成了资源的创建以及释放工作,从而简化了我们对 JDBC 的使用。 它还可以帮助我们避免一些常见的错误,比如忘记关闭数据库连接。 JdbcTemplate 将完成 JDBC 核心处理流程,比如 SQL 语句的创建、执行,而把 SQL 语句的生成以及查询结果的提取工作留给我们的应用代码。 它可以完成 SQL 查询、更新以及调用存储过程,可以对 ResultSet 进行遍历并加以提取。 它还可以捕获 JDBC 异常并将其转换成 org.springframework.dao 包中定义的,通用的,信息更丰富的异常。

使 用 JdbcTemplate 进 行 编 码 只 需 要 根 据 明 确 定 义 的 一 组 契 约 来 实 现 回 调 接 口 。 PreparedStatementCreator 回调接口通过给定的 Connection 创建一个 PreparedStatement,包含 SQL 和任何相关的参数。 CallableStatementCreateor 实现同样的处理,只不过它创建的是CallableStatement。 RowCallbackHandler 接口则从数据集的每一行中提取值。

我们可以在 DAO 实现类中通过传递一个 DataSource 引用来完成 JdbcTemplate 的实例化,也可以在 Spring 的 IOC 容器中配置一个 JdbcTemplate 的 bean 并赋予 DAO 实现类作为一个实例。需要注意的

是DataSource 在 Spring 的 IOC 容器中总是配制成一个 bean,第一种情况下,DataSource bean 将

传递给 service,第二种情况下 DataSource bean 传递给 JdbcTemplate bean。

7.NamedParameterJdbcTemplate 类为 JDBC 操作增加了命名参数的特性支持,而不是传统的使用(’?’)作为参数的占位符。NamedParameterJdbcTemplate 类对 JdbcTemplate 类进行了封装, 在底层,JdbcTemplate 完成了多数的工作。

开发ORM框架

ORM(对象关系映射Object Relation Mapping)。说的就是讲已经持久化的数据内容转换为一个Java对象,用Java对象来描述对象与对象之间的关系和数据内容。
常见的ORM框架:

  • Hibernate
  • MyBatis
  • JPA
  • SpringJDBC

Hibernate 全自动档 不需要写一句SQL语句、烧油(牺牲性能)
MyBatis 手自一体(半自动) 支持单表映射,多表关联需要配置,轻量级一些
SpringJDBC 手动挡 包括SQL语句,映射都是要自己实现的(最省油的)
喜欢轻装上阵 Spring本来就是万能胶IOC/AOP/DI/MVC/JDBC/BigData/Cloud/Boot, 因为Spring形成一个生态

为什么要自己写ORM框架?

  • 解决实际的业务问题(根据业务需要)
  • 自定义需求,如果要直接第三方开源框架的话,需要进行二次开发
  • 解决团队成员之间水平参差不齐的问题
  • 可以实现统一的管理、监控、排错等等一系列底层操作

实际场景应用

以某平台大数据检测系统为例:
痛点:

  • 数据吞吐量大
  • 数据存储方式多样化
  • 数据源需要频繁切换
  • API无法统一
    系统架构图:

通过实现最底层的类解决问题:

1、统一方法名
select
insert
delete
update

find/get/load/query

//约定
如果是删、改,以ID作为唯一的检索条件,如果没有ID,那么要先查出来得到ID

2、统一参数
如果是做条件查询 QueryRule(自己封装)
批量更新和插入 ,方法名以All结尾 参数为List
删、改、插一条数据 ,参数用 T

3、统一返回结果

所有的分页操作返回Page

所有的集合查询返回 List

所有的单条查询返回 T

所有的ID采用Long类型

所有的删除、修改、增加操作返回boolean

对外输出都用ResultMsg

4、 统一封装Freamwork
java extends core common
javax.core.common.utils 操作工具包
javax.core.common.config 统一配置
javax.core.common.doc 文档生成

javax.core.common.jdbc JDBC依赖
javax.core.common.redis Redis
javax.core.common.mongodb MongoDB
javax.core.common.hbase Hbase

只要是Spring相关的配置都以 application- 开头
建议不要把所有的东西在一个文件中,这样不便于团队开发的维护

aop 配置切面,代理规则的
beans 配置单例对象的
common 配置通用的配置
context 主入口
db 数据库相关
web 跟页面打交道的、拦截器、过滤器、监听器、模板


具体的工具类读者可以在本文末给出的Github地址获取

实现ORM操作

public class JdbcTest {public static void main(String[] args) {//        List<?> result = select(Member.class);Member condition = new Member();condition.setName("tom");
//        condition.setAge(20);List<?> result = select(condition);System.out.println(Arrays.toString(result.toArray()));}//我框架问世的时候,你的Member类都还没有从石头缝里蹦出来private static List<?> select(Object condition){try{Class<?> entityClass = condition.getClass();//1、加载驱动类Class.forName("com.mysql.jdbc.Driver");//2、建立连接Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/gupaoedu_demo?characterEncoding=UTF-8&rewriteBatchedStatements=true","root","123456");Table table = entityClass.getAnnotation(Table.class);//3、创建语句开始事务//为了简便,暂时用select * from 代替,不要说我不严谨,OK?String sql = "select * from " + table.name();StringBuffer where = new StringBuffer(" where 1=1 ");Field[] fields = entityClass.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);Object value = field.get(condition);if(null != value) {Class<?> clazz = field.getType();if(String.class == clazz){where.append(" and " + field.getName() + " = '" + value + "'");}else{where.append(" and " + field.getName() + " = " + value);}}}System.out.println(sql + where.toString());PreparedStatement pstmt = conn.prepareStatement(sql + where.toString());//4、执行语句集ResultSet rs = pstmt.executeQuery();//5、获取结果集//ORM:数据表中的记录要复制到Java的Object中//ORM原理:反射机制//自动赋值//拿到一共有多少个列List<Object> result = new ArrayList<Object>();int columnCount = rs.getMetaData().getColumnCount();while (rs.next()){ //游标//===========Begin ORM ============Object instance = entityClass.newInstance();for (int i = 1; i <= columnCount; i ++) {String columnName = rs.getMetaData().getColumnName(i);Field field = entityClass.getDeclaredField(columnName);field.setAccessible(true);//数据类型映射非常关键
//                    Object type = field.getType();
//                    if(type == Long.class){
//                        field.set(instance,rs.getLong(columnName));
//                    }else if(String.class == type){
//                        field.set(instance,rs.getString(columnName));
//                    }else if(Integer.class == type){
//                        field.set(instance,rs.getInt(columnName));
//                    }field.set(instance,rs.getObject(columnName));//各自的厂商实现自己的链接//MySQL为例,以下类型Java语言中是不存在的//bigint ,由开发厂商自动就映射好了//varchar//int
//                    System.out.println(rs.getObject(columnName).getClass());}//===========End ORM ==============result.add(instance);}//System.out.println(Arrays.toString(result.toArray()));//6、关闭结果集、关闭语句集、关闭连接rs.close();pstmt.close();conn.close();return  result;}catch(Exception e){e.printStackTrace();}finally {//关闭资源要在Finally块中}return  null;}}

Member类:

@Entity
@Data
@Table(name="t_member")
public class Member implements Serializable{@Id private Long id;private String name;private String addr;private Integer age;
}

手写ORM框架升级

基于Spring5实现ORM
初衷:

  • 单表查询不写一句SQL,自动生成
  • 查询的结果自动映射为Java对象

1、我要传一个复杂的查询条件,怎么传?
想到了用对象来传,但是有问题
a)、跨表联查的条件
b)、无法携带判断逻辑的运算符
c)、or 或者 and 无法区分

2、自动映射类型判断麻烦

用rs.getObject()方法

3、跨多种数据库如何统一API

4、数据源的动态切换如何来做?
AbstractRoutingDataSource数据源自动路由,执行数据连接以前,根据业务规则来动态获取到一个连接
从而达到动态切换数据库连接的效果
为了操作方便,每次执换完,执行成以后,会将数据源恢复到默认的设置

5、SQL注入
1、首先将QueryRule构建好,把所有的查询条件保存到一个ruleList中
2、再写一个工具类QueryRuleSqlBuilder,循环ruleList对每一个条件分别处理processAnything,主要是构建where后面语句
3、process以后propertisList 保存诸如 and name = ? , values tom,利用索引位置相同,来进行一一对应

根据以上问题,实际解决如下:

framework作为我们封装的自定义ORM框架
demo作为数据持久化层
javax.core.common作为公共基础包
resources作为配置文件
test作为测试类
由于篇幅过长,我讲本节代码地址放到下方,并且加以注释说明,读者可以自行测试

疑问解析

问:selectbysql就相当于没有使用框架,不符合之前统一参数,统一查询方法拉?
答:不推荐使用,如果一定要多表查询,只有两种方案
a) 写SQL语句
b) 查多次,在内存中处理数据关系,一般会在Service中进行处理
在Java代码中处理,会让程序员更加容易理解
如果给大串SQL,这后来接手的人直接想死
我见过一条SQL语句有10行之长的,我直接看晕了
----------- 在我的团队中,极少数使用多表关联查询 -------------

问:······这个PK主键传过来怎么用?有什么好处
答:<T extends Serializable,PK extends Serializable> 传于不传不影响功能
目的:返回结果不需要再手动的强制类型转换

问:如用老师这个框架来组装sql,复杂查询的话,会不会难以组装,
比如说查某个表的字段是另外一个表的条件,以此类推多个表的话?
答:这就是属于多表查询

问:连接操作,还有如果表没有主键PK 还需要传?
答:只要用的我框架,每个表必须有主键,哪怕是自增
为了降低程序设计的复杂度

后记

springORM github代码地址
本系列参考的Spring源码版本是:Spring 5.0.2.RELEASE

SSM源码分析之Spring11-手写SpringORM相关推荐

  1. C# list集合 重复元素 索引_源码分析专题10-纯手写JDK不同版本下的List接口

    纯手写集合框架(下一篇:LinkeList原理) 集合框架介绍 说明:对于以上的框架图有如下几点说明 1.所有集合类都位于java.util包下.Java的集合类主要由两个接口派生而出:Collect ...

  2. SSM源码分析之Spring05-DI实现原理(基于Annotation 注入)

    目录导航 前言 注解的引入 AnnotationConfigApplicationContext 对注解Bean初始化 AnnotationConfigApplicationContext注册注解Be ...

  3. 框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)...

    一.为什么要提供配置的方法 经过前面的手写Spring IOC.手写Spring DI.手写Spring AOP,我们知道要创建一个bean对象,需要用户先定义好bean,然后注册到bean工厂才能创 ...

  4. SSM源码分析之Mybatis02-Mapper与BestPractice

    Mybatis源码分析02-Mapper与BestPractice 前言 Mapper文件解读 BestPractice 总结 前言 Mapper文件解读 namespace 关联到接口方法,区分类似 ...

  5. vue filter对象_学习vue源码(3) 手写Vue.directive、Vue.filter、Vue.component方法

    一.Vue.directive Vue.directive(id,[definition]); 1)参数 { string } id{ Function | Object } [ definition ...

  6. 源码来袭:bind手写实现

    JavaScript中的this指向规则 源码来袭:call.apply手写实现与应用 理解建议:如果对this指向规则不了解的话,建议先了解this指向规则,最好还能对call和apply的使用和内 ...

  7. onclick 源码_精读:手写React框架 解析Hooks源码

    写在开头: 去年发表过一篇手写React,带diff算法,异步setState队列的文章,有一位阿里的朋友在下面评论,让我可以用hooks实现一次,也很简单,我当时觉得,这人有病,现在回过头来看,还是 ...

  8. android米聊手写和涂鸦源码,Android访米聊手写和涂鸦源码

    Android访米聊手写和涂鸦源码 \请下载源代码,只上传Android访米聊手写和涂鸦源码源程序列表内容,如果需要此程序,请点击-下载,下载需要资料源代码. Android访米聊手写和涂鸦源码.ra ...

  9. ceph bluestore源码分析:非对齐写逻辑

    文章目录 环境 原理说明 总结 环境 ceph:12.2.1 场景:ec 2+1 部署cephfs,执行如右写模式:dd if=/dev/zero of=/xxx/cephfs bs=6K count ...

最新文章

  1. php7 数组与字符串,第7天 PHP字符串、数组
  2. 【组队学习】【26期】图神经网络
  3. 【Android WebSocket】Android 端 WebSocket 基本用法 ( 添加依赖和权限 | 创建 WebSocketClient 客户端类 | 建立连接并发送消息 )
  4. 【解题报告】【HDOJ1392】【Graham凸包】Surround the Trees
  5. python环境配置与pytorch下载
  6. [Vue源码分析] Virtual DOM
  7. jquery插件dataTables自增序号。
  8. 苹果 M1 “痛失” Windows 11!
  9. 15 CT04创建特性程序DUMP解决方法
  10. IntelliJ IDEA设置代码提示(常用快捷键)
  11. 一个屌丝程序猿的人生(三十九)
  12. 如何下载谷歌地图高程数据
  13. 2019.7月面试题总结(三)
  14. Linux命令大全(一)(详解)系统管理、系统设置及文档编辑
  15. 为什么智能手机中被撕的永远是华为!
  16. JavaScript(第三天)—爱创课堂专业前端培训
  17. 在iPhone上使用3D Touch
  18. 立体视觉相机使用JetsonTX2提升图像
  19. 审批流程设计方案-介绍(一)
  20. 计算机应用技术怎样为祖国做出贡献,计算机应用技术

热门文章

  1. web自动化测试有哪些工具和框架?
  2. Saber仿真软件平台下的蔡氏电路验证测试
  3. linux网卡限速tc,Linux使用tc对网络进行限速
  4. MOSFET选型注意事项及应用实例
  5. JAVA变量与数据类型
  6. 【原】机器学习公开课 目录(课程笔记、测验习题答案、编程作业源码)...持续更新......
  7. H323Client-1 H323协议族概述
  8. 用scratch2.0编射击游戏
  9. Struts2 框架简介
  10. C++ QT结合FFmpeg实战开发视频播放器-15换肤功能的后台实现