声明式事务
事务机制
提供接口,不同的数据库访问技术使用不同的接口实现
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager t=new JpaTransactionManager();
t.setDataSource(dataSource());
return t;
}
new DataSourceTransactionManager(); jdbc
new JpaTransactionManager(); jpa
new HibernateTransactionManager(); hibernate
new JdoTransactionManager(); jdo
new JtaTransactionManager(); 分布式事务
声明式事务
即:使用注解来选择需要使用事务的方法
@Transactional #表明该方法需要一个事务
也可以注解在类上,此类的所有public方法都是开启事务的。如果同时开启,则方法级别覆盖类级别的。
注解方法在被调用的时候,开启一个新的事务,无异常运行结束后,提交这个事务
@Configuration
@EnableTransactionManagement //开启声明式注解
simpleJpaRepository 在类级别定义了 @Transactional(readOnly = true)
在save,delete相关的操作重写了 @Transactional,此时 readOney=false
默认为false,指定当前事务是否是 只读事务
@Transactional(rollbackFor={IllegalArgumentException.class})
指定哪个或者那些异常可以引起事务回滚
spring Boot的事务支持
JDBC springBoot为我们定义了
@Bean
@ConditionalOnMissingBean //bean不存在就创建
@ConditionalOnBean({DataSource.class})//当dataSourrce存在
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(this.dataSource);
}
使用JPA的时候
@Bean
@ConditionalOnMissingBean({PlatformTransactionManager.class}) //当这个类没有配置的时候
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
自动开启注解事务
@Configuration 配置
//存在这些类才配置
@ConditionalOnClass({TransactionTemplate.class, PlatformTransactionManager.class})
//判断这个类是单例的
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
//自动配置,在这些类后面
@AutoConfigureAfter({JtaAutoConfiguration.class, JpaBaseConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class TransactionAutoConfiguration {
}
所以在实战中不需要 @TransactionAutoConfiguration
事务实战
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
实体类
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private String address;
} //get set 无参 全参
dao
public interface PersonRepository extends JpaRepository<Person, Long> {
}
service
public interface DemoService {
public Person savePersonWithRollBack(Person person);
public Person savePersonWithoutRollBack(Person person);
}
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
PersonRepository personRepository; //1
@Transactional(rollbackFor={IllegalArgumentException.class}) //2
public Person savePersonWithRollBack(Person person){
Person p =personRepository.save(person);
if(person.getName().equals("汪云飞")){
throw new IllegalArgumentException("汪云飞已存在,数据将回滚"); //3
}
return p;
}
@Transactional(noRollbackFor={IllegalArgumentException.class}) //4
public Person savePersonWithoutRollBack(Person person){
Person p =personRepository.save(person);
if(person.getName().equals("汪云飞")){
throw new IllegalArgumentException("汪云飞虽已存在,数据将不会回滚");
}
return p;
}
}
rollbackFor 属性,数据回滚
noRollbackFor属性,数据不回滚
action
@RestController
public class MyController {
@Autowired
DemoService demoService;
@RequestMapping("/rollback")
public Person rollback(Person person){ //1
return demoService.savePersonWithRollBack(person);
}
@RequestMapping("/norollback")
public Person noRollback(Person person){//2
return demoService.savePersonWithoutRollBack(person);
}
}
@SpringBootApplication
public class Ch84Application {
public static void main(String[] args) {
SpringApplication.run(Ch84Application.class, args);
}
}
application.properties
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot
#1
spring.jpa.hibernate.ddl-auto=update
#2
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
数据缓存Cache
重复地获取相同的数据的时候,
我们一次又一次的请求数据库 或者 远程服务,导致大量的时间耗费在数据库查询或者远程方法调用上
CacheManager 用来统一不同的接口,是 各种缓存技术抽象接口。
@Bean
public EhCacheCacheManager cacheCacheManager(CacheManager ehCacheCacheManager){
return new EhCacheCacheManager(ehCacheCacheManager);
}
声明式缓存注解
@Cach eable ,先查看缓存中是否有数据,如果有数据,直接返回数据。 如果没有调用方法,并且放进缓存。使用放入的
@Cache Put ,无论怎样,都将方法的返回值,放到缓存中。和 Cach eable 保持一致。这个是放入
@Cache Evict, 将一条或多条数据从缓存中删除
@Caching , 可以通过@Caching 注解组合 多个注解策略在一个方法上。
开启注解:
@Configuration
@EnableCaching
public class AppConfig {
}
spring boot支持
org.springframework.boot.autoconfigure.cache;
EhCacheCacheConfiguration EhCacheCacheManager
GenericCacheConfiguration 对应:SimpleCacheManager。GenericCache配置的Simple
GuavaCacheConfiguration GuavaCacheManager
HazelcastCacheConfiguration HazelcastCacheManager
InfinispanCacheConfiguration infinispanCacheManager
JCacheCacheConfiguration JCacheCacheManager
NoOpCacheConfiguration NoOpCacheManager 不使用
RedisCacheConfiguration RedisCacheManager
SimpleCacheConfiguration ConcurrentMapCacheManager simple下配置的是concurrentMap。默认值
new SimpleCacheManager(); Collection来存储缓存,测试用的
new ConcurrentMapCacheManager(); ConcurrentMap当做缓存
new NoOpCacheManager(); 测试,不使用
new EhCacheCacheManager(); EhCacheCache
new GuavaCacheManager(); 谷歌的GuavaCache
new HazelcastCacheManager(); 无法new, Hazelcast
new JCacheCacheManager(); Apache jcs的 JCacheCache
new RedisCacheManager(); 无法new
spring.cache前缀
spring.cache.type=redis
public enum CacheType {
/**
* Generic caching using 'Cache' beans from the context.
*/
GENERIC,
/**
* EhCache backed caching.
*/
EHCACHE,
/**
* Hazelcast backed caching
*/
HAZELCAST,
/**
* Infinispan backed caching.
*/
INFINISPAN,
/**
* JCache (JSR-107) backed caching.
*/
JCACHE,
/**
* Redis backed caching.
*/
REDIS,
/**
* Guava backed caching.
*/
GUAVA,
/**
* Simple in-memory caching.
*/
SIMPLE,
/**
* No caching.
*/
NONE;
}
spring:
cache:
type: redis
cache-names: myCache #程序启动时候,缓存名称
ehcache:
config: ehcache.xml #配置文件的路径
hazelcast:
config: ehcache.xml
infinispan:
config: ehcache.xml
jcache:
config: ehcache.xml
provider: #xxx ,当有多个jcache实现在路径中的时候,指定jcache实现
guava:
spec: #guava specs
只需要在项目中导入相关缓存技术的包,并且配置类使用 @EnableCaching开户缓存即可。
缓存实战
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!--
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
实体类 ,启动类,配置文件,dao
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private String address;
}//get set 无参构造,全参数构造
@SpringBootApplication
@EnableCaching //记得要开启缓存
public class Ch85Application {
public static void main(String[] args) {
SpringApplication.run(Ch85Application.class, args);
}
}
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@192.168.31.183\:49161\:xe
spring.datasource.username=system
spring.datasource.password=oracle
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
public interface PersonRepository extends JpaRepository<Person, Long> {
}
service
public interface DemoService {
public Person save(Person person);
public void remove(Long id);
public Person findOne(Person person);
}
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
PersonRepository personRepository;
@Override
@CachePut(value = "people", key = "#person.id") //放入
public Person save(Person person) {
Person p = personRepository.save(person);
System.out.println("为id、key为:"+p.getId()+"数据做了缓存");
return p;
}
@Override
@CacheEvict(value = "people")//2 删除
public void remove(Long id) {
System.out.println("删除了id、key为"+id+"的数据缓存");
//这里不做实际删除操作
}
@Override
@Cacheable(value = "people", key = "#person.id")//3 使用
public Person findOne(Person person) {
Person p = personRepository.findOne(person.getId());
System.out.println("为id、key为:"+p.getId()+"数据做了缓存");
return p;
}
}
如果没有指定key,则方法参数作为key,保存到缓存中
控制器
@RestController
public class CacheController {
@Autowired
DemoService demoService;
@RequestMapping("/put")
public Person put(Person person){
return demoService.save(person);
}
@RequestMapping("/able")
public Person cacheable(Person person){
return demoService.findOne(person);
}
@RequestMapping("/evit")
public String evit(Long id){
demoService.remove(id);
return "ok";
}
}
配置文件和测试
src/main/resources/ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<cache name="people" maxElementsInMemory="1000" />
</ehcache>
http://localhost:8080/able?id=22 //两次访问,第二次压根就不执行service代码,就返回结果了
http://localhost:8080/put?name=汪云飞2&age=24 //返回了一个ID
http://localhost:8080/able?id=35 //使用返回的ID查询,也是直接返回结果
http://localhost:8080/evit?id=35 //删除了id、key为35的数据缓存
http://localhost:8080/able?id=35 //在此访问,才会查询数据库
切换缓存技术:
ehcache , 切换成 guava缓存
切换成redis:
spring-boot-starter-redis
boot将会为我们配置redisCacheManager,以及redisTemplate的bean