Spring-data-jpa 学习笔记(一)详解编程语言
Spring家族越来越强大,作为一名javaWeb开发人员,学习Spring家族的东西是必须的。在此记录学习Spring-data-jpa的相关知识,方便后续查阅。
一、spring-data-jpa的简单介绍SpringData : Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
SpringData 项目所支持 NoSQL 存储:
JPA Spring Data : 致力于减少数据访问层 (DAO) 的开发量, 开发者唯一要做的就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个 UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User 对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
Spring Data JPA 进行持久层(即Dao)开发一般分三个步骤:
声明持久层的接口,该接口继承 Repository(或Repository的子接口,其中定义了一些常用的增删改查,以及分页相关的方法)。 在接口中声明需要的业务方法。Spring Data 将根据给定的策略生成实现代码。 在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 jpa:repositories 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" modelVersion 4.0.0 /modelVersion groupId com.zxy /groupId artifactId springdata-demo /artifactId version 0.0.1-SNAPSHOT /version !-- 全局属性配置 -- properties project.source.encoding utf-8 /project.source.encoding project.build.sourceEncoding UTF-8 /project.build.sourceEncoding !-- 防止控制输出台中文乱码 -- argLine -Dfile.encoding=UTF-8 /argLine /properties dependencies !-- junit_jar包依赖 -- dependency groupId junit /groupId artifactId junit /artifactId version 4.11 /version !--保留到测试 -- scope test /scope /dependency !-- spring-data-jpa相关依赖 (这个依赖自动把一堆spring的东西依赖进来了,所有可以不需要再引入spring的包)-- dependency groupId org.springframework.data /groupId artifactId spring-data-jpa /artifactId version 1.11.7.RELEASE /version /dependency !-- Hibernate -- dependency groupId org.hibernate /groupId artifactId hibernate-core /artifactId version 5.0.11.Final /version /dependency dependency groupId org.hibernate /groupId artifactId hibernate-entitymanager /artifactId version 5.0.11.Final /version /dependency !-- https://mvnrepository.com/artifact/com.mchange/c3p0 -- dependency groupId com.mchange /groupId artifactId c3p0 /artifactId version 0.9.5.2 /version /dependency !-- mysql驱动 -- dependency groupId mysql /groupId artifactId mysql-connector-java /artifactId version 5.1.29 /version /dependency /dependencies build plugins !-- 编译插件 -- plugin groupId org.apache.maven.plugins /groupId artifactId maven-compiler-plugin /artifactId version 2.5.1 /version configuration !-- 源码用1.8 -- source 1.8 /source !-- 打成jar用1.8 -- target 1.8 /target encoding utf-8 /encoding /configuration /plugin /plugins /build /project
(这个依赖自动把一堆spring的东西依赖进来了,所有可以不需要再引入spring的包)--
artifactId mysql-connector-java /artifactId
这里我解释下为何不添加Spring的其他的依赖,主要是spring-data-jpa这个依赖了一堆spring相关的依赖。见下图就明白了
这个整合很重要,我在网上找了好久,没找到特别好的demo;
因此特意把这一步记录下来。
1 首先我们添加一个和数据库相关的properties文件;新建
db.properties文件,内容如下
jdbcUrl=jdbc:mysql://localhost:3306/springdata?useUnicode=true characterEncoding=utf8 driver >jdbcUrl=jdbc:mysql://localhost:3306/springdata?useUnicode=true characterEncoding=utf8
2 然后我们需要新建一个Spring的配置文件,因此新建一个
applicationContext.xml文件。里面大致配置的东西有:
配置数据源dataSource 配置JPA的EntityManagerFactory, 这里面有个包扫描,是扫描实体类那一层的 配置事务管理器transactionManager 配置支持注解的事务 配置SpringData这里包扫描是扫描dao层,扫描那些定义的接口文件里面的具体内容如下:
?xml version="1.0" encoding="UTF-8"? beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd" !-- 配置自动扫描的包,扫描service层,service上面有注解就直接被spring容器实例化 -- context:component-scan base-package="com.zxy.service"/ !-- 1. 配置数据源 -- context:property-placeholder location="classpath:db.properties"/ bean id="dataSource" property name="jdbcUrl" value="${jdbcUrl}"/ property name="driverClass" value="${driverClass}"/ property name="user" value="${user}"/ property name="password" value="${password}"/ property name="initialPoolSize" value="${initialPoolSize}"/ property name="maxPoolSize" value="${maxPoolSize}"/ /bean !-- 2. 配置 JPA 的 EntityManagerFactory -- bean id="entityManagerFactory" property name="dataSource" ref="dataSource"/ property name="jpaVendorAdapter" bean /bean /property !-- 配置包扫描,扫描实体 -- property name="packagesToScan" value="com.zxy.entity"/ property name="jpaProperties" props !-- 生成的数据表的列的映射策略 -- prop key="hibernate.ejb.naming_strategy" org.hibernate.cfg.ImprovedNamingStrategy /prop !-- hibernate 基本属性 -- prop key="hibernate.dialect" org.hibernate.dialect.MySQL5InnoDBDialect /prop prop key="hibernate.show_sql" true /prop prop key="hibernate.format_sql" true /prop prop key="hibernate.hbm2ddl.auto" update /prop /props /property /bean !-- 3. 配置事务管理器 -- bean id="transactionManager" property name="entityManagerFactory" ref="entityManagerFactory"/ /bean !-- 4. 配置支持注解的事务 -- tx:annotation-driven transaction-manager="transactionManager"/ !-- 5. 配置 SpringData,需要加入 jpa 的命名空间 -- !-- base-package: 扫描 Repository Bean 所在的 package -- jpa:repositories base-package="com.zxy.dao" entity-manager-factory-ref="entityManagerFactory" /jpa:repositories /beans61xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"!-- 配置自动扫描的包,扫描service层,service上面有注解就直接被spring容器实例化 --prop key="hibernate.ejb.naming_strategy" org.hibernate.cfg.ImprovedNamingStrategy /propprop key="hibernate.dialect" org.hibernate.dialect.MySQL5InnoDBDialect /propjpa:repositories base-package="com.zxy.dao" entity-manager-factory-ref="entityManagerFactory"
com.zxy.test包中新建一个
TestConfig的类,在类里面我们写单元测试的代码。主要内容有:
通过静态代码块创建 ClassPathXmlApplicationContext对象,让它读取applicationContext.xml,这样就启动了Spring容器 通过Spring容器获取dataSource对象,如果成功获取,说明整合成功了。
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; import javax.sql.DataSource; import org.junit.Test; * 整合效果测试类 * @author ZENG.XIAO.YAN * @date 2017年9月14日 下午11:01:20 * @version v1.0 public class TestConfig { private static ApplicationContext ctx ; static { // 通过静态代码块的方式,让程序加载spring的配置文件 ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); /** 测试spring容器是否实例化了数据源 。如果实例化了,说明Spring容器整合没问题 */ @Test public void testDataSouce() throws SQLException { DataSource dataSouce = (DataSource) ctx.getBean("dataSource"); System.out.println("数据源:"+ dataSouce); System.out.println("连接:"+ dataSouce.getConnection()); }
import org.springframework.context.support.ClassPathXmlApplicationContext;
ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
/** 测试spring容器是否实例化了数据源 。如果实例化了,说明Spring容器整合没问题 */
entityManagerFactory
这个对象是否起作用,这个对象起作用了就会去扫描com.zxy.eneity下面的实体类。测试方法如下:
有一个前提,数据库必须先创建。这里springdata这个数据库我先创建了 给实体类加上注解,然后开启Hibernate自动建表功能,启动Spring容器;如果数据库自动建表成功,那就整合成功
在框架整合完成后,我们就可以开始使用SpringData了,在(3)中我们新建了一个Person实体类,我们就利用这个Person类来展开讲解。
com.zxy.dao
层声明接口,接口中定义我们要的方法,且接口继承Repository接口或者是Repository的子接口,这样就可以操作数据库了。但是在接口中定义的方法是要符合一定的规则的,这个规则在后面会讲到。其实我们也可以写接口的实现类,这个在后续也会讲解。
先新建一个名为
PersonDao的接口,它继承Repository接口;继承
Repository接口
的时候那两个泛型需要指定具体的java类型。第一个泛型是写实体类的类型,这里是Person;第二个泛型是主键的类型,这里是Integer。 在这个接口中定义一个叫做getById(Integer id)的方法,然后我们后面在调用这个方法测试下。
import org.springframework.data.repository.Repository; import org.springframework.data.repository.RepositoryDefinition; import com.zxy.entity.Person; * PersonDao * @author ZENG.XIAO.YAN * @date 2017年9月18日 下午4:25:39 * @version v1.0 * 1.Repository是一个空接口,即是一个标记接口 * 2.若我们定义的接口继承了Repository,则该接口会被IOC容器识别为一个Repository Bean * 注入到IOC容器中,进而可以在该接口中定义满足一定规则的接口 * [email protected] 注解来替代Repository接口 [email protected](domain >
* 2.若我们定义的接口继承了Repository,则该接口会被IOC容器识别为一个Repository Bean
@RepositoryDefination
来代替继承接口Repository接口,这里不做过多介绍这个注解,更多和该注解的相关知识请查阅相关资料。
由于我们数据库中
jpa_persons这个表还没数据,先在这表中手动插入几条测试数据。
com.zxy.test层新建一个名为
TestQucikStart的测试类。还是采用静态代码快的方式来加载Spring配置文件的方式来使用Spring容器,在后续贴的代码中,这部分代码可能会不贴出来。这里先声明一下,后续在代码中看到的
ctx是其实就是Spring容器的意思,它都是这样获取的。
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zxy.dao.PersonDao; import com.zxy.entity.Person; * SpringData快速入门测试类 * @author ZENG.XIAO.YAN * @date 2017年9月18日 下午5:33:42 * @version v1.0 public class TestQuickStart { private static ApplicationContext ctx ; static { // 通过静态代码块的方式,让程序加载spring的配置文件 ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); /** 测试PersonDao中定义的getById的方法能否查询出结果 */ @Test public void testGetById() { PersonDao personDao = ctx.getBean(PersonDao.class); Person person = personDao.getById(1); System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId()); }
import org.springframework.context.support.ClassPathXmlApplicationContext;
ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId());
测试的结果如下图所示,我们只声明了接口和定义了方法就从数据库查到了数据,这就是SpringData的强大之处。
三、SpringData方法定义规范 通过上面的QucikStart的案例,我们了解到在使用SpringData时只需要定义Dao层接口及定义方法就可以操作数据库。但是,这个Dao层接口中的方法也是有定义规范的,只有按这个规范来,SpringData才能识别并实现该方法。下面来说说方法定义的规范。
简单条件查询:查询某一个实体或者集合 按照SpringData规范,查询方法于find|read|get开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:属性首字母需要大写。 支持属性的级联查询;若当前类有符合条件的属性, 则优先使用, 而不使用级联属性。 若需要使用级联属性, 则属性之间使用 _ 进行连接。
下面来看个案例吧,操作的实体依旧上面的Person,下面写个通过id和name查询出Person对象的案例。
在
PersonDao这个接口中,定义一个通过id和name查询的方法
// 通过id和name查询实体,sql: select * from jpa_persons where id = ? and name = ? Person findByIdAndName(Integer id, String name);
// 通过id和name查询实体,sql: select * from jpa_persons where id = ? and name = ?
在
TestQucikStart这个测试类中,写个单元测试方法testFindByIdAndName来测试这个dao层的方法是否可用
public void testGetByIdAndName() { PersonDao personDao = ctx.getBean(PersonDao.class); Person person = personDao.findByIdAndName(1, "test0"); System.out.println(person); }
直接在接口中定义方法,如果符合规范,则不用写实现。目前支持的关键字写法如下:
下面直接展示个案例来介绍下这些方法吧,
PersonDao接口新增代码如下:
// where id ? or birth ? List Person findByIdIsLessThanOrBirthLessThan(Integer id, Date birth); // where email like ? List Person findByEmailLike(String email); // 也支持count查询 long countByEmailLike(String email);8
在
TestQucikStart中添加以下2个单元测试方法,测试dao层接口中的方法是否可用
public void testFindByEmailLike() { PersonDao personDao = ctx.getBean(PersonDao.class); List Person list = personDao.findByEmailLike("test%"); for (Person person : list) { System.out.println(person.getEmail()); /** 测试findByIdIsLessThanOrBirthLessThan方法 */ @Test public void testFindByIdIsLessThanOrBirthLessThan() { PersonDao personDao = ctx.getBean(PersonDao.class); List Person list = personDao.findByIdIsLessThanOrBirthLessThan(3, new Date()); for (Person person : list) { System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId() + ",birth=" + person.getBirth()); }
List Person list = personDao.findByIdIsLessThanOrBirthLessThan(3, new Date());
Dao层接口中定义的方法支持级联查询,下面通过一个案例来介绍这个级联查询:
修改Person类,添加address属性,使Person和Address成多对一的关系,设置外键列名为address_id ,添加的代码如下图:
public void testFindByAddressId() { PersonDao personDao = ctx.getBean(PersonDao.class); // 查出地址id为1的person集合 List Person list = personDao.findByAddressId(1); for (Person person : list) { System.out.println(person.getName() + "---addressId=" + person.getAddress().getId()); }
通过以上的学习,掌握了在接口中定义方法的规则,我们就可以定义出很多不用写实现的方法了。这里再介绍下查询方法的解析的流程吧,掌握了这个流程,对于定义方法有更深的理解。
假如创建如下的查询:
findByUserDepUuid(),框架在解析该方法时,流程如下:
首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc 先判断 userDepUuid(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续往下走 从右往左截取第一个大写字母开头的字符串(此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复这一步,继续从右往左截取;最后假设 user 为查询实体的一个属性 接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据 Doc.user.depUuid 的取值进行查询;否则继续按照步骤3的规则从右往左截取,最终表示根据 Doc.user.dep.uuid 的值进行查询。
可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以
明确在级联的属性之间加上 _ 以显式表达意图,比如
findByUser_DepUuid() 或者
findByUserDep_uuid() 。
2
方法参数
带特殊参数的查询 特殊的参数: 还可以直接在方法的参数上加入分页或排序的参数,比如: Page UserModel findByName(String name, Pageable pageable) List UserModel findByName(String name, Sort sort); [email protected] 通过上面的学习,我们在dao层接口按照规则来定义方法就可以不用写方法的实现也能操作数据库。但是如果一个条件查询有多个条件时,写出来的方法名字就太长了,所以我们就想着不按规则来定义方法名。我们可以[email protected],[email protected],将查询语句声明在注解中,也可以查询到数据库的数据。 (1)使用Query结合jpql语句实现自定义查询在PersonDao接口中声明方法,放上面加上Query注解,注解里面写jpql语句,代码如下:
// 自定义的查询,直接写jpql语句; 查询id ? 或者 名字 like?的person集合 @Query("from Person where id ?1 or name like ?2") List Person testPerson(Integer id, String name); // 自定义查询之子查询,直接写jpql语句; 查询出id最大的person @Query("from Person where id = (select max(p.id) from Person as p)") Person testSubquery();7
PersonDao personDao = ctx.getBean(PersonDao.class); List Person list = personDao.testPerson(2, "%admin%"); for (Person person : list) { System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId()); System.out.println("===============分割线==============="); Person person = personDao.testSubquery(); System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId()); }
System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId());
System.out.println("查询结果: name=" + person.getName() + ",id=" + person.getId());
(2)索引参数和命名参数
在写jpql语句时,查询条件的参数的表示有以下2种方式: 索引参数方式如下图所示,索引值从1开始,查询中 ?x 的个数要和方法的参数个数一致,且顺序也要一致
命名参数方式(推荐使用这种方式)如下图所示,可以用 :参数名 的形式,在方法参数中使用@Param( 参数名 )注解,这样就可以不用按顺序来定义形参
说一个
特殊情况,那就是自定义的Query查询中
jpql语句有
like查询时,可以直接把
%
号写在参数的前后,这样传参数就不用把
%
号拼接进去了。使用案例如下,调用该方法时传递的参数直接传就ok。
(3)[email protected] 如果你不熟悉jpql语句,你也可以写sql语句查询,[email protected]
nativeQuery=true。直接来看案例吧
dao层接口写法如下图所示
测试代码这里直接不贴了,下面是控制台中打印的语句和结果
[email protected](1)@Modifying注解的使用 @[email protected],可实现个性化更新操作及删除操作;例如只涉及某些字段更新时最为常见。
下面演示一个案例,把
id小于3的person的
name都改为
admin dao层代码如下所示
//可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT //在 @Query 注解中编写 JPQL 语句, 但必须使用 @Modifying 进行修饰. 以通知 SpringData, 这是一个 UPDATE 或 DELETE 操作 //UPDATE 或 DELETE 操作需要使用事务, 此时需要定义 Service 层. 在 Service 层的方法上添加事务操作. //默认情况下, SpringData 的每个方法上有事务, 但都是一个只读事务. 他们不能完成修改操作! @Modifying @Query("UPDATE Person p SET p.name = :name WHERE p.id :id") int updatePersonById(@Param("id")Integer id, @Param("name")String updateName);7
//可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT
//在 @Query 注解中编写 JPQL 语句, 但必须使用 @Modifying 进行修饰. 以通知 SpringData, 这是一个 UPDATE 或 DELETE 操作
//UPDATE 或 DELETE 操作需要使用事务, 此时需要定义 Service 层. 在 Service 层的方法上添加事务操作.
//默认情况下, SpringData 的每个方法上有事务, 但都是一个只读事务. 他们不能完成修改操作!
int updatePersonById(@Param("id")Integer id, @Param("name")String updateName);
由于这个更新操作,只读事务是不能实现的,因此新建PersonService类,在Service方法中添加事务注解。PersonService的代码如下图所示
package com.zxy.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.zxy.dao.PersonDao; * PersonService * @author ZENG.XIAO.YAN * @date 2017年9月20日 下午2:57:16 * @version v1.0 @Service("personService") public class PersonService { @Autowired private PersonDao personDao; @Transactional(readOnly=false) public int updatePersonById(Integer id, String updateName) { return personDao.updatePersonById(id, updateName); }
Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。 对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional 声明 进行多个 Repository 操作时,也应该使它们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层,因此,需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。
六、本文小结 (1)本文只简单介绍了下SpringData,知道了SpringData简化了dao层的代码,我们可以只声明接口就可以完成对数据库的操作。
(2)介绍了一个SpringData的入门案例,其中包含了
需要哪些依赖的jar包,
如何整合Spring-data-jpa以及怎么测试是否整合成功等。 (3)介绍了Dao层接口继承了Repository接口后,该按照什么规则去定义方法就可以被SpringData解析;且展示了SpringData对级联查询的案例。同时也讲解了SpringData解析方法的整个流程。
(4)[email protected],有了这个注解,我们就可以随便定义方法的名字,方法的功能由我们自己写jqpl语句或者是sql语句来实现。在介绍这个注解的时候,也讲解了jpql或者sql中参数可以用索引参数和命名参数的两种方式来表示。
(5)[email protected]@Query注解,实现更新和删除。同时也介绍了SpringData的事务。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/15540.html
cgojavamysqlxml相关文章
- Spring学习笔记(八)——SpringMVC响应数据和结果视图
- Spring学习笔记(九)——SpringMVC实现文件上传
- Spring学习笔记(十)——SpringMVC框架中的拦截器和异常处理
- Spring的学习笔记(十五)——SSM 解决接口跨域问题
- Spring学习笔记(二十)——springboot基于AdminLTE的后台管理系统模板
- Spring学习笔记(二十一)——规范开发:SpringBoot表单验证、AOP切面编程、统一返回结果和异常处理
- Spring学习笔记(二十四)——springboot实现邮箱服务
- Spring学习笔记(二十五)——springboot定时任务和Gson、Date、进制等工具的使用
- Spring学习笔记(三十二)——SpringBoot中cache缓存的介绍和使用
- Spring学习笔记(三十五)——小技巧:配置全局跨域、全局⽇期格式化、读取本地json
- Spring学习笔记(三十七)——Flyway 数据库版本控制
- Spring连接池与事务管理 | Spring学习笔记
- Spring 中 jdbcTemplate 的使用 | Spring学习笔记
- Spring Framework 源码学习笔记(一)
- Spring Framework 源码学习笔记(三)
- Spring Framework 源码学习笔记(五)
- Spring笔记
- batch spring 重复执行_Spring Batch批处理
- 基于AspectJ的AOP应用 | Spring学习笔记2.2
- Spring boot 启动错误:Could not resolve placeholder
- Spring学习笔记四 整合SSH详解编程语言
- Spring错误:org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.B详解编程语言
- spring笔记二详解编程语言
- spring笔记一详解编程语言
- spring的AOP(四)—-Spring AOP 编程(Advisor)详解编程语言
- Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase