springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

一:与JPA规范整合

  jpa是一套orm的规范,提供api接口,hirebnate就是对jpa的一套实现,下面我们看看springboot如何

与jpa整合

1:添加依赖和配置

		<!--5: 整合jpa-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

  

#自动创建表
spring.jpa.hibernate.ddl-auto:update
#打印 sql 语句
spring.jpa.show-sql:true

  

2:定义service 、dao以及实体类

@Service
public class StudentServiceImpl implements StudentService {

	@Autowired
	private StudentDao studentDao;

	@Override
	public Student findById(Integer id) {
		Optional<Student> optional = studentDao.findById(id);
		if(optional.isPresent()){
			return optional.get();
		}
		return null;
	}

	@Override
	public Student insertStudent(Student student) {
		return studentDao.save(student);
	}

}

  

@Repository
public interface StudentDao extends JpaRepository<Student,Integer> {

}

 

@Data
@Entity
@Table(name="student")
public class Student {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column(name="name")
	private String name;

	@Column(name="cardId")
	private String cardId;

}

  

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

服务启动从日志看,会创建表:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

测试类:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

生成insert语句,插入数据:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 与JPA整合完成,很简单

 

二:与atomikos整合,实现分布式事务

  如果同时在一个方法内使用两个数据源,想实现事务,该怎么办?

1:添加依赖和配置

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>

  

# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/enjoy?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root
mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60

# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root
mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60

#日志级别
logging.level.root=info
#所有包下面都以debug级别输出
#logging.level.org.springframework.*=debug

#sql日志
logging.level.com.xiangxue.atomikos.db1.dao=debug
logging.level.com.xiangxue.atomikos.db2.dao=debug
#热部署
#热部署生效
spring.devtools.restart.enabled=true
#设置重启的目录
spring.devtools.restart.additional-paths=src/main/java
#classpath目录下的WEB-INF文件夹内容修改不重启
spring.devtools.restart.exclude=WEB-INF/**

 

将配置封装到对象上,使用@ConfigurationProperties注解,会把mysql.datasource.test1为前缀的配置封装到对应的属性上,维护到spring

容器缓存中

@Data
@ConfigurationProperties(prefix = "mysql.datasource.test1")
public class DBConfig1 {
    private String url;

    private String username;

    private String password;

    private int minPoolSize;

    private int maxPoolSize;

    private int maxLifetime;

    private int borrowConnectionTimeout;

    private int loginTimeout;

    private int maintenanceInterval;

    private int maxIdleTime;

    private String testQuery;
}

  

创建数据源DataSource、sqlSessionFactory以及sqlSessionTemplate对象,并且添加@MapperScan扫描,将MapperFactoryBean维护到spring容器中,

key为Mapper文件的全限定名类名,value为MapperFactoryBean对象

@Configuration
@MapperScan(basePackages = "com.xiangxue.jack.atomikos.db1.dao", sqlSessionFactoryRef = "test1SqlSessionFactory",sqlSessionTemplateRef="test1SqlSessionTemplate")
public class Db1Config {

    @Autowired
    DBConfig1 testConfig;

    @Bean(name = "test1DataSource")
    public DataSource testDataSource() {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("test1DataSource");
        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        try {
            xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "test1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

  

创建Mapper类:

public interface CommonMapper1 {

    @Select("select * from people")
    List<People> listPeoples();

    @Insert("insert into people(name) values(#{name})")
    int addPeople(People people);
}

  

另一个数据源也需要创建DataSource、SqlSessionFactory、sqlSessionTemplate对象,扫描对应的Mapper接口文件路径,和上一个数据源创建方式一样

 

创建一个service的测试类:在方法上添加@Transactional注解,方法内部,两个不同的mapper新增数据,两个mapper对应不同的数据源

@Service
public class AreaServiceImpl implements AreaService {

    @Autowired
    private CommonMapper1 commonMapper1;

    @Autowired
    private CommonMapper2 commonMapper2;

    @Autowired
    TransactionManager transactionManager;

    @Transactional
    public int saveArea(ConsultConfigArea area) {
        System.out.println(transactionManager);
        JtaTransactionManager jtaTransactionManager = (JtaTransactionManager)transactionManager;
        System.out.println(jtaTransactionManager.getUserTransaction());
        UserTransaction userTransaction = jtaTransactionManager.getUserTransaction();

        People people = new People();
        people.setName("Lucy");
        int count = commonMapper1.addPeople(people);
        System.out.println("插入一条People,count:"+count);

        Student student = new Student();
        student.setCardId("122334");
        student.setName("hello");
        int count1 = commonMapper2.addStudent(student);
        System.out.println("插入一条Student,count1:"+count1);
        int i = 10/0;
        return count;
    }
}

  

先把会导致异常的这行代码注释掉:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 数据插入成功! 

现在把会导致运行时异常的代码放开,看看效果

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

数据没有插入,说明回滚成功!

 

三:与redis整合

1:添加依赖和配置

		<!--6:整合redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

  

#Redis数据库索引(默认为0)
spring.redis.database=0
#redis服务器地址
spring.redis.host=127.0.0.1
#redis暴露端口
spring.redis.port=6379
#redis服务器连接密码
spring.redis.password=
#连接池最大连接
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间
spring.redis.timeout=5000

  

2:创建缓存管理类

@Configuration
@EnableCaching
public class RedisConfig {

    //缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration).build();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }
}

  

创建使用类:

@Component
public class RedisServiceImpl implements RedisService{

	@Autowired
	private UserMapper userMapper;

	@Cacheable(cacheNames = "redisCache",key = "'jack'+ #id")
	@Override
	public String queryData(String id) {
		System.out.println("RedisServiceImpl -- queryData");
		List<User> list = userMapper.queryUserById(id);
		return JSON.toJSONString(list);
	}

	@CachePut(cacheNames = "redisCache",key = "'jack'+ #id")
	@Override
	public String putCache(String id) {
		System.out.println("RedisServiceImpl -- putCache");
		User user = new User();
		user.setName("Tom");
		user.setAge("30");
		user.setId(id);
		userMapper.updateUser(user);
		return JSON.toJSONString(user);
	}
}

 

下面我们把redis服务器和应用启动,测试一下:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 首先查询redis缓存,缓存中没有数据,然后查询数据库,数据缓存到redis,返回数据,我们来看看redis服务器

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 数据已经被放入redis缓存中,我们再次从页面查询一次:

没有查询数据库,queryData还是上次查询数据库的日志,但是页面显示正常,说明查询的是redis服务器

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

那如果我们想修改数据库的数据,会怎么样?

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 我们来看看redis内存库的数据会不会变化:

springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

 

 

说明修改数据库的数据,redis会同步更新

这样springboot和redis的整合就完成了!!!

 

上一篇:SpringBoot 分布式事务框架atomikos


下一篇:整合Atomikos、Quartz、Postgresql的踩坑日记