Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一。Mybatis是一个十分轻量好用的ORM框架。Redis是当今十分主流的分布式key-value型数据库,在web开发中,我们常用它来缓存数据库的查询结果。 本篇博客将介绍如何使用Spring-Boot快速搭建一个Web应用,并且采用Mybatis作为我们的ORM框架。为了提升性能,我们将Redis作为Mybatis的二级缓存。为了测试我们的代码,我们编写了单元测试,并且用H2内存数据库来生成我们的测试数据。通过该项目,我们希望读者可以快速掌握现代化Java Web开发的技巧以及最佳实践。 环境
Spring-Boot新建项目首先,我们需要初始化我们的Spring-Boot工程。通过Intellij的Spring Initializer,新建一个Spring-Boot工程变得十分简单。首先我们在Intellij中选择New一个Project: 然后在选择依赖的界面,勾选Web、Mybatis、Redis、Mysql、H2: 新建工程成功之后,我们可以看到项目的初始结构如下图所示: Spring Initializer已经帮我们自动生成了一个启动类—— @SpringBootApplicationpublic class SpringBootMybatisWithRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootMybatisWithRedisApplication.class, args); } }
新建API接口接下来,我们要编写Web API。假设我们的Web工程负责处理商家的产品(Product)。我们需要提供根据product id返回product信息的get接口和更新product信息的put接口。首先我们定义Product类,该类包括产品id,产品名称name以及价格price: public class Product implements Serializable { private static final long serialVersionUID = 1435515995276255188L; private long id; private String name; private long price; // getters setters} 然后我们需要定义Controller类。由于Spring Boot内部使用Spring MVC作为它的Web组件,所以我们可以通过注解的方式快速开发我们的接口类: @RestController@RequestMapping('/product')public class ProductController { @GetMapping('/{id}') public Product getProductInfo( @PathVariable('id') Long productId) { // TODO return null; } @PutMapping('/{id}') public Product updateProductInfo( @PathVariable('id') Long productId, @RequestBody Product newProduct) { // TODO return null; } } 我们简单介绍一下上述代码中所用到的注解的作用:
这里我们只定义了接口,实际的处理逻辑还未完成,因为product的信息都存在数据库中。接下来我们将在项目中集成mybatis,并且与数据库做交互。 集成Mybatis配置数据源首先我们需要在配置文件中配置我们的数据源。我们采用mysql作为我们的数据库。这里我们采用yaml作为我们配置文件的格式。我们在resources目录下新建application.yml文件: spring: 由于Spring Boot拥有自动配置的特性,我们不用新建一个DataSource的配置类,Sping Boot会自动加载配置文件并且根据配置文件的信息建立数据库的连接池,十分便捷。
配置Mybatis我们已经通过Spring Initializer在pom.xml中引入了 # mybatis配置mybatis: config-location: classpath:mybatis-config.xml 然后我们在resources目录下新建 <!DOCTYPE configuration 接下来,我们再在resourses目录下新建mappers目录,并且新建 @Mapperpublic interface ProductMapper { Product select( @Param('id') long id); void update(Product product); }
访问数据库完成了Mybatis的配置之后,我们就可以在我们的接口中访问数据库了。我们在 @RestController@RequestMapping('/product')public class ProductController { @Autowired 然后在你的mysql中插入几条product的信息,就可以运行该项目看看是否能够查询成功了。 至此,我们已经成功地在项目中集成了Mybatis,增添了与数据库交互的能力。但是这还不够,一个现代化的Web项目,肯定会上缓存加速我们的数据库查询。接下来,将介绍如何科学地将Redis集成到Mybatis的二级缓存中,实现数据库查询的自动缓存。 集成Redis配置Redis同访问数据库一样,我们需要配置Redis的连接信息。在application.yml文件中增加如下配置: spring: redis: # redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突 database: 3 # redis服务器地址(默认为localhost) host: localhost # redis端口(默认为6379) port: 6379 # redis访问密码(默认为空) password: # redis连接超时时间(单位为毫秒) timeout: 0 # redis连接池配置 pool: # 最大可用连接数(默认为8,负数表示无限) max-active: 8 # 最大空闲连接数(默认为8,负数表示无限) max-idle: 8 # 最小空闲连接数(默认为0,该值只有为正数才有作用) min-idle: 0 # 从连接池中获取连接最大等待时间(默认为-1,单位为毫秒,负数表示无限) max-wait: -1 上述列出的都为常用配置,读者可以通过注释信息了解每个配置项的具体作用。由于我们在pom.xml中已经引入了 将Redis作为二级缓存Mybatis的二级缓存原理本文不再赘述,读者只要知道,Mybatis的二级缓存可以自动地对数据库的查询做缓存,并且可以在更新数据时同时自动地更新缓存。 实现Mybatis的二级缓存很简单,只需要新建一个类实现 该接口共有以下五个方法:
接下来,我们新建 public class RedisCache implements Cache { private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final String id; // cache instance id 讲解一下上述代码中一些关键点:
这样,我们就实现了一个优雅的、科学的并且具有Spring Style的Redis缓存类。 开启二级缓存接下来,我们需要在 <?xml version='1.0' encoding='UTF-8' ?><!DOCTYPE mapper PUBLIC '-////DTD Mapper 3.0//EN' 'http:///dtd/mybatis-3-mapper.dtd'><mapper namespace='com.wooyoo.learning.dao.mapper.ProductMapper'> <!-- 开启基于redis的二级缓存 --> <cache type='com.wooyoo.learning.util.RedisCache'/> <select id='select' resultType='Product'> SELECT * FROM products WHERE id = #{id} LIMIT 1 </select> <update id='update' parameterType='Product' flushCache='true'> UPDATE products SET name = #{name}, price = #{price} WHERE id = #{id} LIMIT 1 </update></mapper>
测试配置H2内存数据库至此我们已经完成了所有代码的开发,接下来我们需要书写单元测试代码来测试我们代码的质量。我们刚才开发的过程中采用的是mysql数据库,而一般我们在测试时经常采用的是内存数据库。这里我们使用H2作为我们测试场景中使用的数据库。 要使用H2也很简单,只需要跟使用mysql时配置一下即可。在application.yml文件中: ---spring: profiles: test 为了避免和默认的配置冲突,我们用 在上述配置中,schema.sql用于存放我们的建表语句,data.sql用于存放insert的数据。这样当我们测试时,h2就会读取这两个文件,初始化我们所需要的表结构以及数据,然后在测试结束时销毁,不会对我们的mysql数据库产生任何影响。这就是内存数据库的好处。另外,别忘了在pom.xml中将h2的依赖的scope设置为test。
编写测试代码因为我们是通过Spring Initializer初始化的项目,所以已经有了一个测试类—— Spring Boot提供了一些方便我们进行Web接口测试的工具类,比如 @RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)@ActiveProfiles(profiles = 'test')public class SpringBootMybatisWithRedisApplicationTests { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test public void test() { long productId = 1; Product product = restTemplate.getForObject('http://localhost:' + port + '/product/' + productId, Product.class); assertThat(product.getPrice()).isEqualTo(200); Product newProduct = new Product(); long newPrice = new Random().nextLong(); newProduct.setName('new name'); newProduct.setPrice(newPrice); restTemplate.put('http://localhost:' + port + '/product/' + productId, newProduct); Product testProduct = restTemplate.getForObject('http://localhost:' + port + '/product/' + productId, Product.class); assertThat(testProduct.getPrice()).isEqualTo(newPrice); } } 在上述测试代码中:
查看测试结果我们在Intellij中点击执行测试用例,测试结果如下: 真棒,显示的是绿色,说明测试用例执行成功了。 总结本篇文章介绍了如何通过Spring Boot、Mybatis以及Redis快速搭建一个现代化的Web项目,并且同时介绍了如何在Spring Boot下优雅地书写单元测试来保证我们的代码质量。当然这个项目还存在一个问题,那就是mybatis的二级缓存只能通过flush整个DB来实现缓存失效,这个时候可能会把一些不需要失效的缓存也给失效了,所以具有一定的局限性。 |
|