在介绍Spring Data JPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对II主,使用0/R映射(Object-Relational Mapping)技术实现数据访问,O/R映射即将领域模型类和数据库的表进行映射,通过程序操作对象而实现表数据操作的能力,让数据访问操作无须关注数据库相关的技术。
随着Hibernate的盛行,Hibernate主导了EJB3.0的JPA规范,JPA即Java Persistence API。JPA是一个基于0/R映射的标准规范(目前最新版本是JPA2.1)。所谓规范即只定义标准规则(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件提供商的实现打交道。JPA的主要实现由Hibernate、Eclipse Link和OpenJPA等,这也意味着我们只要使用JPA来开发,无论是哪一个开发方式都是一样的。
Spring Data JPA是Spring Data的一个子项目,它通过提供基于JPA的Repository极大地减少了JPA作为数据访问方案的代码量。
在JPA中主要是使用函数名来分别数据库操作的,比如findByUsername,就是通过username查找记录,再比如findByUsernameAndPassword就是通过用户名密码访问数据,所以在JPA 中,函数的取名还是比较重要的,这部分也可通过代码提示来查看,因为他会根据数据库表字段和一些关键字推荐可用的部分函数名。当然JPA中也有一些原先就定义好的方法,比如findAll,save等方法。
在JPA定义函数名需要遵循下列规定:
KeywordSampleSQLAndfindByLastnameAndFirstnamewhere x.lastname = ?1 and x.firstname = ?2OrfindByLastnameOrFirstnamewhere x.lastname = ?1 or x.firstname = ?2Is,EqualsfindByFirstnamefindByFirstnameIsfindByFirstnameEqualswhere x.firstname = 1?BetweenfindByStartDateBetweenwhere x.startDate between 1? and ?2LessThanfindByAgeLessThanwhere x.age < ?1LessThanEqualfindByAgeLessThanEqualwhere x.age <= ?1GreaterThanfindByAgeGreaterThanwhere x.age > ?1GreaterThanEqualfindByAgeGreaterThanEqualwhere x.age >= ?1AfterfindByStartDateAfterwhere x.startDate > ?1BeforefindByStartDateBeforewhere x.startDate < ?1IsNullfindByAgeIsNullwhere x.age is nullIsNotNull,NotNullfindByAge(Is)NotNullwhere x.age not nullLikefindByFirstnameLikewhere x.firstname like ?1NotLikefindByFirstnameNotLikewhere x.firstname not like ?1StartingWithfindByFirstnameStartingWithwhere x.firstname like ?1 (parameter bound with appended %)EndingWithfindByFirstnameEndingWithwhere x.firstname like ?1 (parameter bound with prepended %)ContainingfindByFirstnameContainingwhere x.firstname like ?1 (parameter bound wrapped in %)OrderByfindByAgeOrderByLastnameDescwhere x.age = ?1 order by x.lastname descNotfindByLastnameNotwhere x.lastname <> ?1InfindByAgeIn(Collection ages)where x.age in ?1NotInfindByAgeNotIn(Collection age)where x.age not in ?1TruefindByActiveTrue()where x.active = trueFalsefindByActiveFalse()where x.active = falseIgnoreCasefindByFirstnameIgnoreCasewhere UPPER(x.firstame) = UPPER(pom.xml文件中添加jpa依赖的包,很数据库连接库,这里使用的是mysql数据库。
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>配置连接信息,在application.yml文件中添加数据库连接的username,password等信息。这部分信息和之前在SpringMVC中的信息几乎是相同的。
spring: datasource: url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver jpa: database: MYSQL hibernate: ddl-auto: update show-sql: trueSpringBoot中默认的连接池好像是org.apache.tomcat.jdbc.pool.DataSource。 在yml文件的代码提示中支持的连接池好像还有有dbcp,dbcp2,tomcat,hikari,但是按照提示之后好像在控制台看不到dbcp,dbcp2,tomcat,hikari这几个关键词,所以也不知道配置是否成功。
这里我还自己配置了阿里云的Druid连接池,就目前查看资料,感觉比较好的连接池有Druid和hikari,但是在实际中是哪几个比较好还不好说,下面是Druid的配置。
在pom.xml文件中添加所需要的依赖库:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.2</version> </dependency>然后在application.yml配置下面的信息
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver jpa: database: MYSQL hibernate: ddl-auto: update show-sql: true在springboot中配置这部分的信息相对而言还是比较简洁的
这个映射类还是很简单的,主要是上面有一个@NamedQuery注解,使用这个注解是可以自定义SQL语句来对数据库进行操作的。然后根据name(这里的name是hhh,不是Test.hhh)来调用这部分信息。
这里面就是实现与操作数据库的操作。继承的JpaRepository中的前一个为数据库表对应的实体类,第二个为该表的主键的类型。看到最后有何叫hhh的函数,这个就是之前在Test类中自定义的SQL查询语句。然后在Jpa中自定义还可以在本类中是实现,就是findTestById,在函数名之前用@Query自定义SQL语句。
在这个类中主要是调用TestRepository中的方法。然后启动SpringBoot服务,分别访问对应的URL就可以得到对应的数据。在本类中,还有两个部分,分别是排序和分页这部分的内容,在JPA中也得到了很好地支持,这部分东西看代码还是容易看的,就不做解释了。
Transactional中的属性主要是有下面几个。
属性含义Propagation(默认REQUIRED)Propagation定义了事务的生命周期主要有以下选项:REQUIRED:方法A调用时没有事务新建一个事务,当在方法A调用另外一个方法B的时候,方法B将使用相同的事务;如果方法B发生异常需要数据回滚的时候,整个事务数据回滚REQUIRES_NEW:对于方法A和B,在方法调用的时候无论是,否有事务都开启一个新的事务;这样如果方法B有异常不会导致,方法A的数据回滚NESTED:和REQUIRES_NEW类似,但支持JDBC,不支持JPA或HibernateSUPPORTS:方法调用时有事务就用事务,没事务就不用事务NOT_SUPPORTED:强制方法不在事务中执行,若有事务,在方法调用到结束阶段事务都将会被挂起NEVER:强制方法不在事务中执行,若有事务则抛出异常MANDATORY:强制方法在事务中执行,若无事务则抛出异常Isolation(默认DEFAULT)Isolation(隔离)决定了事务的完整性,处理在多事务对相同数据下的处理机制,主要包含下面的隔离级别(当然我们也不可以随意设置,这要看当前数据库是否支持)READ_UNCOMMITTED:对于在A事务里修改了一条记录但没有提交事务,在B事务可以读取到修改后的记录。可导致脏读、不可重复读以及幻读 READ_COMMITTED:只有当在A事务里修改了一条记录且提交事务之后,B事务才可以读取到提交后的记录;阻止脏读,但可能导致不可重复读和幻读REPEATABLE_READ:不仅能实现 READ_COMMITTED 的功能,而且还能阻止到A事务读取了一条记录,B事务将不允许修改这条记录;阻止脏读和不可重复读,但可出现幻读SERIALIZABLE:此级別下亊务足顺序执行的,可以避免上述级别的缺陷,似开销较大DEFAULT:使用当前数据库的默认隔离界级别,如Oracle,SQL Server 是 READ_COMMITTED; Mysql是REPEATABLE_READtimeouttimeout指定事务过期时间,默认为当前数据库的事务过期时间readOnly指定当前事务是否只读事务,默认falserollbackFor指定哪个或者哪些异常可以引起事务回滚noRollbackFor指定哪个或者哪些异常不可以引起事务回滚但是在这里很多属性是不方便测试的,这里就不做测试了。 在这里需要注意的是使用的是org.springframework.transaction.annotation.Transactional;包中的Transactional注解,若是使用javax.transaction.Transactional;的注解,该包中只有rollbackOn和dontRollbackOn这儿两个属性,其他的属性是没有的,当然使用这个包中的这两个属性也是完成可以的,但是其他的属性就没有办法测试了。 下面是测试代码(是在上面的那个部分完成之后才能做这个的)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TranscationalController { @Autowired private TestRepository testRepository; /** * rollbackFor 抛出异常之后回滚数据,即新纪录不会被写进数据库 * * @Transactional 使用的是org.springframework.transaction.annotation.Transactional; * 不是 javax.transaction.Transactional; */ @RequestMapping("/transaction_1") @Transactional(rollbackFor = { IllegalArgumentException.class }) public void transaction_1() { testRepository.save(new Test("transaction_1", "transaction_1")); throw new IllegalArgumentException("\rollbackFor"); } /** * noRollbackFor 抛出异常之后,不会滚数据,即新纪录还是会被写进数据库 */ @RequestMapping("/transaction_2") @Transactional(noRollbackFor = { IllegalArgumentException.class }) public void transaction_2() { testRepository.save(new Test("transaction_2", "transaction_2")); throw new IllegalArgumentException("\noRollbackFor"); } /** * readOnly = true * 会出现异常:Queries leading to data modification are not allowed */ @RequestMapping("/transaction_3") @Transactional(readOnly = true) public Test transaction_3() { return testRepository.save(new Test("transaction_3", "transaction_3")); } /** * 其他的与transaction相关的还有isolation,timeout和propagation,这三个不好测试,感觉用默认的就挺好用的了 */ }测试缓存的方法是,若缓存中存在该记录,则重新访问一个URL获取同一个数据,则在后台的console中是不会输出SQL语句的,就是不会重新从数据库中获取数据,二是从缓存中直接拿去数据。
这是我的源码,有兴趣的可以下载看看http://download.csdn.net/download/q15150676766/9924308