第1章
Spring数据源码环境搭建与核心类介绍
本章将讲解对Spring与数据源码环境搭建相关操作,包含Spring Data Access中的spring-jdbc模块和spring-orm模块的基础操作内容。
1.1 spring-jdbc测试环境搭建
本节将对spring-jdbc测试环境的搭建进行说明,通过本节的操作将会得到一个spring-jdbc源码分析的测试环境。在本节中会对spring-jdbc的两种环境搭建进行说明:第一种是SpringXML模式;第二种是Spring注解模式。
1.1.1 spring-jdbc基于SpringXML环境搭建
本节将搭建基于SpringXML模式的spring-jdbc环境,首先在IDEA中右击项目顶层,选择New→Module选项,如图1.1所示。
图1.1 选择Module工程
选择Module选项后会弹出如图1.2所示的对话框,单击Gradle选项,勾选Java复选框,再单击Next按钮进入下一步。
单击Next按钮后弹出如图1.3所示的对话框,输入Name、GroupId、ArtifactId和Version这四项数据内容。
图1.2 选择Gradle和Java工程
图1.3 输入Name等属性
输入完成后单击 Finish按钮完成工程创建。在工程创建完成以后需要对build.gradle文件进行修改,主要修改的是dependencies节点相关数据,向dependencies节点添加如下数据内容。
dependencies {
compile(project(":spring-tx"))
compile(project(":spring-jdbc"))
compile(project(":spring-context"))
compile(project(":spring-orm"))
implementation 'mysql:mysql-connector-java:5.1.46'
implementation 'org.apache.tomcat:tomcat-dbcp:9.0.1'
implementation 'org.hibernate:hibernate-core:5.4.2.Final'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
在本章中所涉及的spring-jdbc和spring-orm环境都将基于该工程进行。在本节使用的是SpringXML模式搭建spring-jdbc测试环境,在resources文件夹下创建jdbcTemplateConfiguration
.xml文件,并且向该文件中添加如下内容。
在上述SpringXML配置文件中对dataSource和jdbcTemplate进行了自定义,当填写完上述内容之后需要填写一些自定义的配置,需要填写的内容有如下4项。
(1)driverClassName:表示数据库驱动名称。
(2)url:表示JDBC链接地址。
(3)username:表示数据库用户名。
(4)password:表示数据库密码。
读者根据自身环境配置完成上述四项参数信息后就可以编写具体的测试代码。创建一个Java类,类名为TUserEntity,具体代码如下。
@Entity
@Table(name = "t_user")
public class TUserEntity {
private Long id;
private String name;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
public Long getId() {
return id;
}
@Basic
@Column(name = "name", nullable = true, length = 255)
public String getName() {
return name;
}
}
在完成TUserEntity类的编写操作后需要新建数据库表,关于t_user的建表语句如下。
CREATE TABLE t_user (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
在完成t_user建表语句的编写后需要将其放在数据库中执行,本例中采用的是MySQL数据库,直接将其复制到MySQL命令行中执行即可。完成数据表的创建后需要向该表中添加数据,添加数据的SQL语句代码如下。
INSERT INTO t_user(id, name) VALUES (1, 'ac');
在完成数据表数据初始化后需要进行最后的JdbcTemplate使用,创建一个Java类,类名为JdbcTemplateWithXmlDemo,该类具体代码如下。
public class JdbcTemplateWithXmlDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("jdbcTemplateConfiguration.xml");
JdbcTemplate bean = context.getBean(JdbcTemplate.class);
List query = bean.query("select * from t_user", new
RowMapper() {
@Override
public TUserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
long id = rs.getLong("id");
String name = rs.getString("name");
TUserEntity tUserEntity = new TUserEntity();
tUserEntity.setId(id);
tUserEntity.setName(name);
return tUserEntity;
}
});
System.out.println();
}
}
完成JdbcTemplateWithXmlDemo类的编写后需要将这个类启动,将断点放在System.out
.println()这一行,查看query的数据信息,具体信息如图1.4所示。
图1.4 查询结果1
从图1.4中可以发现,query的数据信息和数据库中的数据信息相同,此时完成了spring-jdbc基于SpringXML模式的测试环境搭建全过程。
1.1.2 spring-jdbc基于Spring注解模式环境搭建
本节将介绍spring-jdbc基于Spring注解模式的测试环境。首先创建一个Spring配置类,类名为SpringJdbcConfig,向该类中添加如下代码。
@Configuration
@ComponentScan("com.github.source.hot.data.jdbc")
public class SpringJdbcConfig {
@Bean
public DataSource mysqlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(
DataSource mysqlDataSource
) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(mysqlDataSource);
return jdbcTemplate;
}
}
在完成SpringJdbcConfig类的编写后需要根据当前所在环境修改下面4个方法的参数。
(1)方法setDriverClassName:设置数据库驱动名称。
(2)方法setUrl:设置数据库链接地址。
(3)方法setUsername:设置数据库用户名。
(4)方法setPassword:设置数据库密码。
读者根据自身环境配置完成上述4个方法的参数信息后就可以编写具体的测试代码。创建一个Java类,类名为JdbcTemplateWithAnnotationDemo,具体代码如下。
public class JdbcTemplateWithAnnotationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringJdbcConfig.class);
JdbcTemplate bean = context.getBean(JdbcTemplate.class);
List query = bean.query("select * from t_user",
new RowMapper() {
@Override
public TUserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
long id = rs.getLong("id");
String name = rs.getString("name");
TUserEntity tUserEntity = new TUserEntity();
tUserEntity.setId(id);
tUserEntity.setName(name);
return tUserEntity;
}
});
System.out.println();
}
}
完成JdbcTemplateWithAnnotationDemo类的编写后需要将这个类启动,将断点放在System
.out.println()这一行,查看query的数据信息,具体信息如图1.5所示。
图1.5 查询结果2
从图1.5中可以发现,query的数据信息和数据库中的数据信息相同,此时完成了spring-jdbc基于Spring注解模式的测试环境搭建全过程。
1.2 spring-orm测试环境搭建
在本节将对spring-orm测试环境的搭建进行说明,通过本节将会得到一个spring-orm源码分析的测试环境。在本节中会对spring-orm的两种环境搭建进行说明:第一种是Spring XML模式;第二种是Spring注解模式。
1.2.1 spring-orm基于SpringXML环境搭建
本节将介绍spring-orm基于SpringXML模式的测试环境。首先需要在resources文件夹下创建hibernate5Configuration.xml文件,并且向该文件中添加如下内容。
update
org.hibernate.dialect.MySQL5Dialect
update
在本节中使用的是Hibernate框架作为ORM框架,在hibernate5Configuration.xml文件中关于sessionFactory的配置信息有如下内容。
(1)packagesToScan:表示实体对象的扫描路径。
(2)hibernateProperties:表示hibernate的属性值。
在hibernate5Configuration.xml文件中关于hibernateProperties属性做出了3个数据的设置。
(1)hibernate.hbm2ddl.auto:表示启动时的操作。
(2)hibernate.dialect:表示数据库方言,在本例中采用的是MySQL5。数据库方言是指各个数据库厂商提供的SQL。使用数据库方言的意义在于让ORM框架适应不同的厂商数据库,属于一个转译模块。
(3)hibernate.hibernate.hbm2ddl.auto:表示启动时应该进行的操作。
上述第1个属性和第3个属性都表示启动时的操作,具体操作符有4种。
(1)create:表示启动的时候先进行删除操作,再进行创建操作。
(2)create-drop:表示创建,只不过在系统关闭前执行删除操作。
(3)update:这个操作在启动时会检查schema是否一致,如果不一致会做scheme更新。
(4)validate:在启动时验证现有schema与用户配置的hibernate是否一致,如果不一致就抛出异常,并不做更新。
本例中使用的是update操作,在按需填写完成packagesToScan属性和hibernateProperties属性后还需要对dataSource中的相关信息进行配置,需要进行配置的内容有如下4项。
(1)driverClassName:表示数据库驱动名称。
(2)url:表示JDBC链接地址。
(3)username:表示数据库用户名。
(4)password:表示数据库密码。
按需填写完成上述4项配置后需要编写主体测试类,创建类名为SpringOrmXMLDemo的Java文件,向SpringOrmXMLDemo类中添加如下代码。
public class SpringOrmXMLDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("hibernate5Configuration.xml");
SessionFactory bean = context.getBean(SessionFactory.class);
Session session = bean.openSession();
TUserEntity tUserEntity = session.get(TUserEntity.class, 1L);
System.out.println();
}
}
编写完成SpringOrmXMLDemo类后将断点放在 System.out.println()这一行,调试该项目查看tUserEntity变量的数据信息,具体信息如图1.6所示。
从图1.6中可以发现,tUserEntity和数据库中的数据信息相同,至此spring-orm基于SpringXML模式环境搭建完成。
1.2.2 spring-orm基于Spring注解模式环境搭建
本节将介绍spring-orm基于Spring注解模式的测试环境。首先需要编写一个Spring配置类,创建一个类,类名为HibernateConf,具体代码如下。
@Configuration
@EnableTransactionManagement
public class HibernateConf {
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
"");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
@Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "update");
hibernateProperties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.H2Dialect");
hibernateProperties.setProperty(
"hibernate.hibernate.hbm2ddl.auto", "update");
return hibernateProperties;
}
}
在编写完成HibernateConf类后还需要进行自定义配置填写,需要修改的信息如下。
(1)修改方法setPackagesToScan的参数,将其设置为实体类所在的包路径。
(2)修改方法setDriverClassName的参数,将其设置为数据库驱动名称。
(3)修改方法setUrl的参数,将其设置为数据库链接地址。
(4)修改方法setUsername的参数,将其设置为数据库用户名。
(5)修改方法setPassword的参数,将其设置为数据库密码。
(6)修改hibernateProperties方法中的数据。
完成HibernateConf类中的自定义配置修改后需要进行启动类的编写,创建一个Java类,类名为SpringOrmAnnotationDemo,具体代码如下。
@Configuration
@EnableTransactionManagement
@Import(HibernateConf.class)
public class SpringOrmAnnotationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringOrmAnnotationDemo.class);
SessionFactory bean = context.getBean(SessionFactory.class);
Session session = bean.openSession();
TUserEntity tUserEntity = session.get(TUserEntity.class, 1L);
System.out.println();
}
}
编写完SpringOrmAnnotationDemo类后将断点放在 System.out.println()这一行,调试该项目查看tUserEntity变量的数据信息,具体信息如图1.7所示。
从图1.7中可以发现,tUserEntity和数据库中的数据信息相同,至此spring-orm基于Spring注解模式环境搭建完成。
图1.7 查询结果4
1.3 Spring数据操作中的核心类
本节将介绍Spring数据操作中的核心类。在前面关于测试环境搭建过程中所使用的类都可以视作核心类,例如在spring-jdbc环境搭建过程中使用的DriverManagerDataSource类和JdbcTemplate类等,下面将列出作者认为的核心类。
(1)DriverManagerDataSource,它是一个基于JDBC的标准实现,作用是进行数据源管理。
(2)JdbcTemplate,它是一个基于JDBC的实现,作用是简化传统JDBC的操作流程。
(3)JdbcOperations,封装了JDBC的数据操作。
(4)ResultSetExtractor,它是JdbcTemplate查询方法使用时的回调接口,此接口的实现从ResultSet中获取实际数据。接口的作用是提取数据。
(5)RowCallbackHandler,它用于一行一行地处理ResultSet数据。
(6)RowMapper,用于按行进行映射处理,通过ResultSet进行实际类转换。
(7)SqlProvider,用于提取SQL语句。
(8)HibernateOperations,基于Hibernate进行封装的操作API接口,定义了数据库操作接口。
(9)SessionHolder,用于持有Hibernate中的session对象的对象。
(10)TransactionManager事务管理器接口,子类有PlatformTransactionManager和ReactiveTransactionManager,在非响应式编程的情况下常用的是PlatformTransactionManager。
(11)TransactionExecution事务状态接口,子类有TransactionStatus和ReactiveTransaction,前者适用于非响应式编程,后者适用于响应式编程。
1.4 总结
在本章讲述了spring-jdbc和spring-orm的基本环境搭建,主要围绕SpringXML模式和Spring注解模式进行,此外介绍了Spring数据操作中的核心类。
第2章
JdbcTemplate类分析
本章将对JdbcTemplate类进行分析。JdbcTemplate类是一个基于JDBC进行封装的类,常见的JDBC操作有新增、修改、删除和查询,本章将围绕这4个操作进行分析。
2.1 初识JdbcTemplate类
在本节将对JdbcTemplate类做一个简单的介绍,首先需要查看JdbcTemplate的类图,详细信息如图2.1所示。
在图2.1中可以发现,JdbcTemplate类实现了两个接口JdbcOperations和InitializingBean,在spring-jdbc中JdbcOperations接口定义了JDBC相关操作,按照操作行为分类可以分为下面3类。
(1)执行类,以"execute"字符串开头的方法。
(2)查询类,以"query"字符串开头的方法。
(3)更新类,包含"update"字符串的方法,在JdbcOperations中有批量提交的方法batchUpdate。
关于JdbcOperations接口的实现会在本章的后续部分进行分析,在JdbcTemplate类中还实现了InitializingBean,需要在JdbcTemplate的类图中找到具体的实现方法,实现方法在JdbcAccessor中,具体代码如下。
@Override
public void afterPropertiesSet() {
if (getDataSource() == null) {
throw new IllegalArgumentException("Property 'dataSource' is required");
}
if (!isLazyInit()) {
getExceptionTranslator();
}
}
在上述代码中主要处理流程如下。
(1)判断数据源(DataSource)对象是否为空,如果数据源对象为空则会抛出异常。
(2)判断是不是懒加载的,如果不是懒加载的会获取SQLExceptionTranslator对象。
在上述两个操作流程中,需要关注getExceptionTranslator方法,具体处理代码如下。
public SQLExceptionTranslator getExceptionTranslator() {
SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator;
if (exceptionTranslator != null) {
return exceptionTranslator;
}
synchronized(this) {
exceptionTranslator = this.exceptionTranslator;
if (exceptionTranslator == null) {
DataSource dataSource = getDataSource();
if (dataSource != null) {
exceptionTranslator =
new SQLErrorCodeSQLExceptionTranslator(dataSource);
}
else {
exceptionTranslator = new SQLStateSQLExceptionTranslator();
}
this.exceptionTranslator = exceptionTranslator;
}
return exceptionTranslator;
}
}
这段代码的主要目的是将exceptionTranslator成员变量进行初始化,从上述代码中可以发现,初始化方法有两种方式。
(1)当数据源对象存在时,通过SQLErrorCodeSQLExceptionTranslator的构造方法+数据源对象进行创建。
(2)当数据源对象为空时,通过SQLStateSQLExceptionTranslator的构造方法进行创建。
2.1.1 DataSource分析
在afterPropertiesSet处理方法中提到了一个接口DataSource,DataSource是Java提供的接口,具体包路径是javax.sql.DataSource,在spring-jdbc中由DriverManagerDataSource实现,关于数据源可以简单地理解为存储了数据库账号、数据库密码和数据库地址的存储对象。在前面搭建的测试用例中关于数据源的定义代码如下。
@Bean
public DataSource mysqlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
在这段代码中定义的DataSource对象信息如图2.2所示。
注:在实际开发过程中,链接地址、账户和密码会因开发者的配置不同而不同,具体由开发者配置决定。由于作者采用的是一个线上环境的数据库,因此书中将链接地址、账户和密码信息隐藏。
图2.2 DataSource对象信息
通过图2.2可以发现,通过set方法进行设置的数据已经完成设置。在mysqlDataSource方法中所使用的DataSource实现类是spring-jdbc自带的,在第1章中搭建的测试用例还引入了tomcat-dbcp相关依赖,并对其进行了使用,关于tomcat-dbcp中的数据源配置代码如下。
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
在这个方法中所得到的数据源对象信息如图2.3所示。
图2.3 BasicDataSource对象信息
从图2.3中可以发现,基本的四个元素(数据库链接、数据库账号、数据库密码和数据库驱动)都是存在的,相比spring-jdbc提供的数据源对象,tomcat-dbcp提供的数据源对象有更多属性,有兴趣的读者可以进入源码查看具体作用。
2.1.2 JdbcTemplate的初始化
在前面对DataSource进行了相关分析,接下来将进入JdbcTemplate的初始化分析。通常对于JdbcTemplate初始化是指进行数据源对象的设置,在第1章的测试用例中可以看到两种设置方式。
(1)通过JavaBean进行设置。
(2)通过SpringXML配置进行设置。
下面以通过JavaBean进行设置作为分析目标,具体处理代码如下。
@Bean
public JdbcTemplate jdbcTemplate(
DataSource mysqlDataSource
) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(mysqlDataSource);
return jdbcTemplate;
}
在这段代码中所得到的JdbcTemplate对象信息如图2.4所示。
图2.4 JdbcTemplate对象信息
在图2.4中可以发现数据源对象被成功设置,此外还有一些默认值在JdbcTemplate对象中。在图2.4中需要注意exceptionTranslator对象为null,同时还需要注意lazyInit变量信息为true。再继续向下执行代码,由于此时的数据源对象不为空,因此不会出现IllegalArgumentException异常信息。由于此时lazyInit变量为true,因此不会将exceptionTranslator变量信息进行初始化。如果需要查看exceptionTranslator变量的初始化操作,则需要将代码进行修改,修改后代码如下。
@Bean
public JdbcTemplate jdbcTemplate(
DataSource mysqlDataSource
) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(mysqlDataSource);
jdbcTemplate.setLazyInit(false);
return jdbcTemplate;
}
经过修改后将断点放在JdbcAccessor对象中的afterPropertiesSet方法上就可以查看到exceptionTranslator的数据信息,具体信息如图2.5所示。
图2.5 修改后exceptionTranslator的数据信息
2.2 JdbcTemplate中的执行操作分析
本节将对JdbcTemplate中的执行操作进行分析。在JdbcTemplate中关于执行操作可以使用execute方法,比如可以使用“jdbcTemplate.execute("select * from t_user");”,在JdbcTemplate类中使用execute方法是最常见的,这段代码对应的源码内容如下。
@Override
public void execute(final String sql) throws DataAccessException {
class ExecuteStatementCallback implements StatementCallback