배경
SpringBoot는 오늘날 가장 주류의 Java 웹 개발 프레임 워크 중 하나를 만들었습니다. Mybatis는 매우 가볍고 사용하기 쉬운 ORM 프레임 워크입니다. Redis는 오늘날 매우 주류 분산 키 가치 데이터베이스입니다. 웹 개발에서는 종종 데이터베이스 쿼리 결과를 캐시하는 데 사용합니다.
이 블로그는 SpringBoot를 사용하여 웹 응용 프로그램을 신속하게 구축하고 Mybatis를 ORM 프레임 워크로 사용하는 방법을 소개합니다. 성능을 향상시키기 위해 Redis를 MyBatis의 두 번째 레벨 캐시로 사용합니다. 코드를 테스트하기 위해 단위 테스트를 작성하고 H2 메모리 데이터베이스를 사용하여 테스트 데이터를 생성했습니다. 이 프로젝트를 통해 독자들이 현대 Java 웹 개발의 기술과 모범 사례를 빠르게 마스터 할 수 있기를 바랍니다.
이 기사의 샘플 코드는 github : https://github.com/lovelcp/spring-boot-mybatis-with-redis/tree/master에서 다운로드 할 수 있습니다.
환경
개발 환경 : Mac 10.11
IDE : Intellij 2017.1
JDK : 1.8
스프링 부츠 : 1.5.3. release
REDIS : 3.2.9
MySQL : 5.7
스프링 부츠
새 프로젝트를 만듭니다
먼저 스프링 부츠 프로젝트를 초기화해야합니다. Intellij의 Spring Initializer를 통해 새로운 스프링 부츠 프로젝트를 만드는 것은 매우 쉽습니다. 먼저, 우리는 Intellij에서 새로운 프로젝트를 선택합니다.
그런 다음 인터페이스에서 종속성을 선택하고 웹, mybatis, redis, mysql, h2를 확인하십시오.
새로운 프로젝트가 성공하면 아래 그림과 같이 프로젝트의 초기 구조를 볼 수 있습니다.
Spring Initializer는 시작 클래스 인 SpringBootmyBatiswithRedisApplication을 자동으로 생성하는 데 도움이되었습니다. 이 클래스의 코드는 매우 간단합니다.
@SpringBootApplicationPublic Class SpringBootmyBatiswithRedisApplication {public static void main (String [] args) {springApplication.run (springbootmyBatiswithRedisApplication.class, args); }}@SpringBootApplication 주석은 Spring Boot의 자동 구성 기능을 활성화하는 것을 의미합니다. 좋아, 우리의 프로젝트 스켈레톤은 성공적으로 구축되었으므로 관심있는 독자들은 Intellij를 통해 결과를 시작할 수 있습니다.
새 API 인터페이스를 만듭니다
다음으로 웹 API를 작성합니다. 웹 엔지니어링이 판매자의 제품 (제품)을 처리 할 책임이 있다고 가정 해 봅시다. 제품 ID와 제품 정보를 업데이트하는 PUT 인터페이스를 기반으로 제품 정보를 반환하는 GET 인터페이스를 제공해야합니다. 먼저 제품 ID, 제품 이름 및 가격이 포함 된 제품 클래스를 정의합니다.
공개 클래스 제품은 시리얼이즈 가능한 {개인 정적 최종 최종 긴 시리얼 버니 우드 = 1435515995276255188L을 구현합니다. 개인 긴 ID; 개인 문자열 이름; 개인 긴 가격; // getters setters}그런 다음 컨트롤러 클래스를 정의해야합니다. Spring Boot는 Spring MVC를 내부적으로 웹 구성 요소로 사용하므로 주석을 통해 인터페이스 클래스를 신속하게 개발할 수 있습니다.
@restController @requestMapping ( "/ufduct") public class productController {@getMapping ( "/{id}") 공개 제품 getProductInfo (@pathvariable ( "id") long productId) {// todo return null; } @putMapping ( "/{id}") 공개 제품 업데이트 froductInfo (@PathVariable ( "id") Long ProductID, @RequestBody 제품 NewProduct) {// todo return null; }}위 코드에 사용 된 주석의 기능을 간략하게 소개하겠습니다.
@RestController : 클래스가 컨트롤러이고 REST 인터페이스를 제공 함을 의미합니다. 즉, 모든 인터페이스의 값은 JSON 형식으로 반환됩니다. 이 주석은 실제로 @controller와 @responsebody의 조합 주석으로, 나머지 API를 개발할 수 있습니다.
@requestmapping, @getmapping, @putmapping : 인터페이스의 URL 주소를 나타냅니다. 클래스에 주석이 붙은 @requestmapping 주석은 클래스의 모든 인터페이스의 URL이 /upub즈로 시작한다는 것을 의미합니다. @GetMapping은 이것이 get http 인터페이스임을 의미합니다. @putmapping은 이것이 PUT HTTP 인터페이스임을 의미합니다.
@PathVariable, @RequestBody : 매개 변수의 매핑 관계를 나타냅니다. GET 요청이 액세스 /제품 /123이라고 가정하면, 요청은 getProductInfo 방법에 의해 처리되며, 여기서 URL의 123은 productID에 매핑됩니다. 마찬가지로, PUT 요청 인 경우, 요청 된 본문은 NewProduct 객체에 매핑됩니다.
여기서는 인터페이스 만 정의하며 제품 정보가 데이터베이스에 존재하기 때문에 실제 처리 로직은 아직 완료되지 않았습니다. 다음으로 MyBatis를 프로젝트에 통합하고 데이터베이스와 상호 작용합니다.
mybatis의 통합
데이터 소스를 구성합니다
먼저 구성 파일에서 데이터 소스를 구성해야합니다. MySQL을 데이터베이스로 사용합니다. 여기서는 Yaml을 구성 파일의 형식으로 사용합니다. 리소스 디렉토리에서 새 Application.yml 파일을 만듭니다.
Spring :# Database Configuration DataSource : URL : jdbc : mysql : // {Your_host}/{Your_db} username : {your_username} 암호 : {your_password} driver class-name : org.gjt.mm.mysql.driverSpring Boot에는 자동 구성 기능이 있으므로 새 데이터 소스 구성 클래스를 만들 필요가 없습니다. Spring Boot는 구성 파일을 자동으로로드하고 구성 파일 정보를 기반으로 데이터베이스 연결 풀을 설정합니다. 이는 매우 편리합니다.
저자는 YAML을 구성 파일 형식으로 사용하는 것이 좋습니다. XML은 길고 속성에는 계층 구조가 없습니다. Yaml은 두 가지의 결점을 보완합니다. 또한 Spring Boot가 기본적으로 Yaml 형식을 지원하는 이유입니다.
mybatis를 구성하십시오
Spring Inerializer를 통해 POM.XML에 MyBatis-Spring-Boot-Starte Library를 소개하여 MyBatis를 자동으로 초기화하는 데 도움이됩니다. 먼저 Application.yml에서 mybatis의 관련 구성을 작성합니다.
# myBatis myBatis 구성 : # 매핑 클래스가 위치한 패키지 이름 구성 유형-알리 아스-패키지 : com.wooyoo.learning.dao.dao.dao.dao.dao.dao.dao.dao.dao.dao.dao.dao.dao.Dao.Dao.Dao.Dao.Dao는 Mapper XML 파일이있는 경로를 구성합니다. 여기에는 Mapper-Locations가 있습니다.-Mappers/ProductMapper.xml.
그런 다음 코드에서 ProductMapper 클래스를 정의하십시오.
@MapperPublic Interface ProductMapper {Product Select (@param ( "id") Long ID); 무효 업데이트 (제품 제품);}여기서 @Mapper 주석을 추가하는 한 Spring Boot는 MyBatis를 초기화 할 때 Mapper 클래스를 자동으로로드합니다.
Spring Boot가 인기있는 가장 큰 이유는 자동 구성 기능 때문입니다. 개발자는 개별 구성 요소를 초기화하는 방법에 대한 관심없이 구성 요소 (데이터베이스 연결 정보와 같은) 구성에주의를 기울여야하므로 비즈니스 구현에 집중하고 개발 프로세스를 단순화 할 수 있습니다.
데이터베이스 액세스
MyBatis 구성을 완료 한 후 인터페이스에서 데이터베이스에 액세스 할 수 있습니다. 우리는 ProductController에서 @autowired를 통해 Mapper 클래스를 소개하고 해당 방법을 호출하여 제품에서 쿼리 및 업데이트 작업을 구현합니다. 여기서 우리는 예를 들어 쿼리 인터페이스를 취합니다.
@restController @requestMapping ( "/uppuble") public class productController {@autowired private productMapper productMapper; @GetMapping ( "/{id}") 공개 제품 getProductInfo (@PathVariable ( "id") Long ProductId) {return productMapper.Select (productId); } // 너무 오래 피하고 updateProductInfo의 코드를 생략하십시오}그런 다음 몇 가지 제품 정보를 MySQL에 삽입하면 프로젝트를 실행하여 쿼리가 성공했는지 확인할 수 있습니다.
지금까지 우리는 MyBatis를 프로젝트에 성공적으로 통합하여 데이터베이스와 상호 작용하는 기능을 추가했습니다. 그러나 그것은 충분하지 않습니다. 최신 웹 프로젝트는 캐시에서 데이터베이스 쿼리 속도를 높이게됩니다. 다음으로 Redis를 MyBatis의 2 차 캐시에 과학적으로 통합하여 데이터베이스 쿼리의 자동 캐시를 실현하는 방법을 소개합니다.
통합 된 Redis
Redis를 구성하십시오
데이터베이스에 액세스하는 것과 마찬가지로 Redis 연결 정보를 구성해야합니다. Application.yml 파일에 다음 구성을 추가하십시오.
스프링 : redis : # redis 데이터베이스 인덱스 (기본값은 0). 우리는 다른 데이터베이스 데이터베이스와 충돌을 피하기 위해 색인 3이있는 데이터베이스를 사용합니다. 3 # redis 서버 주소 (기본값은 로컬 호스트) 호스트 : 로컬 호스트 # redis port (기본값은 6379) 포트 : 6379 # redis access password (기본값은 null) 비밀번호 : # redis 연결 시간 초 음수는 무한대를 나타냅니다) Max-Active : 8 # 최대 유휴 연결 수 (기본값은 8, 음수는 무한을 나타냅니다.
위에 나열된 모든 것은 일반적으로 사용되는 구성이며 독자는 주석 정보를 통해 각 구성 항목의 특정 역할을 이해할 수 있습니다. POM.XML에 스프링 부트 스타터 데이터-레디스 라이브러리를 도입 했으므로 스프링 부트는 자동으로 Redis 연결 및 특정 구성 클래스를로드하는 데 도움이됩니다.
org.springframework.boot.autoconfigure.data.redis.redisautoconfiguration. 이 구성 클래스를 통해 기본 레이어가 기본적으로 Jedis 라이브러리를 사용하고 상자 밖에서 Redistemplate 및 StringTemplate을 제공한다는 것을 알 수 있습니다.
Redis를 레벨 2 캐시로 사용하십시오
Mybatis의 2 차 캐싱의 원리는이 기사에서 설명되지 않습니다. 독자는 MyBatis의 보조 캐싱이 데이터베이스 쿼리를 자동으로 캐시 할 수 있으며 데이터를 업데이트 할 때 캐시를 자동으로 업데이트 할 수 있음을 알아야합니다.
Mybatis의 2 차 캐싱 구현은 매우 간단합니다. org.apache.ibatis.cache.cache 인터페이스를 구현하려면 새 클래스 만 만들면됩니다.
이 인터페이스에는 5 가지 방법이 있습니다.
String getId () : MyBatis 캐시 작동 오브젝트의 식별자입니다. 맵퍼는 mybatis 캐시 작동 오브젝트에 해당합니다.
void putobject (객체 키, 객체 값) : 쿼리 결과를 캐시에 넣습니다.
개체 getObject (객체 키) : 캐시에서 캐시 된 쿼리 결과를 가져옵니다.
개체 removeObject (Object Key) : 캐시에서 해당 키와 값을 제거합니다. 롤백 할 때만 해고되었습니다. 일반적으로 우리는 그것을 구현할 필요가 없습니다. 특정 사용 방법은 org.apache.ibatis.cache.decorators.transactionalCache를 참조하십시오.
void clear () : 업데이트가 발생하면 캐시를 지우십시오.
int getsize () : 선택적 구현. 캐시 수를 반환합니다.
readwritelock getReadWritElock () : 선택적 구현. 원자 캐시 작업을 구현하는 데 사용됩니다.
다음으로 캐시 인터페이스를 구현하기위한 새로운 Rediscache 클래스를 만듭니다.
공개 클래스 REASISCACHE 구현 캐시 {개인 정적 최종 로거 로그거 = loggerFactory.getLogger (readiscache.class); 개인 최종 readWritElock readWritelock = 새로운 ReentrantreadWritelock (); 개인 최종 문자열 ID; // 캐시 인스턴스 ID 개인 redistemplate redistemplate; 개인 정적 최종 최종 Long Expire_time_in_minutes = 30; // redis 만료 시간 public readiscache (string id) {if (id == null) {새로운 불법 불법 행위 렉싱 ( "캐시 인스턴스가 ID 필요"); } this.id = id; } @override public String getId () {return id; } / ** * Query 결과를 redis * * @param key * @param value * / @override @suppresswarnings ( "선택 취소") public void putobject (Object Key, Object Value) {redistemplate redistemplate = getRedistemplate (); ValueOperations opsforValue = redistemplate.opsforValue (); opsforvalue.set (키, 값, expire_time_in_minutes, timeUnit.minutes); logger.debug ( "쿼리 결과를 redis에 넣음"); } / ** * redis * @param key * @return * / @override public object getObject (개체 키) {redistemplate redistemplate = getRedistemplate (); ValueOperations opsforValue = redistemplate.opsforValue (); logger.debug ( "redis에서 캐시 된 쿼리 결과를 얻습니다"); return opsforvalue.get (key); } / ** * redis * * @param key * @return * / @override @suppresswarnings ( "선택 취소") 공개 객체 removeObject (객체 키) {redistemplate redistemplate = getRedistemplate (); redistemplate.delete (키); logger.debug ( "Redis에서 캐시 된 쿼리 결과 제거"); 널 리턴; } / ** *이 캐시 인스턴스를 지우는 경우 * / @override public void clear () {redistemplate redistemplate = getRedistemplate (); redistemplate.execute ((readiscallback) connection-> {connection.flushdb (); return null;}); logger.debug ( "Redis의 모든 캐시 된 쿼리 결과를 지우기"); } @override public int getsize () {return 0; } @override public readwritelock getReadWritElock () {return readWritElock; } private redistemplate getRedistemplate () {if (redistemplate == null) {redistemplate = ApplicationContexTholder.getBean ( "redistemplate"); } reture redistemplate를 반환합니다. }}위 코드의 몇 가지 핵심 사항을 설명하겠습니다.
구현 한 두 번째 레벨 캐시에는 ID가있는 생성자가 있어야합니다. 그렇지 않으면 오류 가보고됩니다.
우리는 스프링 캡슐화 된 Redistemplate를 사용하여 Redis를 작동합니다. 레벨 2의 보조 캐시에 Redis를 소개하는 모든 기사는 Jedis 라이브러리를 직접 사용하지만 저자는 이것이 충분한 스프링 스타일이 아니라고 생각합니다. 또한, Redistemplate은 기본 구현을 캡슐화합니다. 앞으로 Jedis를 사용하지 않으면 상단 코드를 수정하지 않고 기본 라이브러리를 직접 교체 할 수 있습니다. 더 편리한 점은 Redistemplate를 사용하여 Redis 연결의 출시에 신경 쓰지 않아도됩니다. 그렇지 않으면 초보자가 연결을 해제하고 응용 프로그램이 고착되는 것을 잊어 버릴 수 있습니다.
Rediscache는 스프링 컨테이너의 콩이 아니기 때문에 Redistemplate을 Autowire를 통해 참조 할 수 없습니다. 따라서이 콩을 얻으려면 컨테이너의 getbean 방법을 수동으로 호출해야합니다. 특정 구현 방법은 GitHub의 코드를 참조하십시오.
우리가 사용하는 Redis 직렬화 방법은 기본 JDK 직렬화입니다. 따라서 데이터베이스 쿼리 객체 (예 : 제품 클래스)는 직렬화 가능한 인터페이스를 구현해야합니다.
이런 식으로, 우리는 스프링 스타일의 우아하고 과학적 및 Redis 캐시 클래스를 구현합니다.
레벨 2 캐시를 켜십시오
다음으로 ProductMapper.xml에서 레벨 2 캐시를 활성화해야합니다.
<? xml version = "1.0"encoding = "utf-8"?> <! doctype mapper public "-// mybatis.org//dtd Mapper 3.0 // en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><<<<<<<<<1mapper 네임 스페이스 = "com.wooyoo.learo.dao.mapper.productmapper"> <!-enable redis 기반 보조 캐시-> <cache type = "com.wooyoo.learning.util.rediscache"/<cache type anable id = "upchant"> {id}} < #} <id} <<id} < "<"id} select "select"select "select"select "select"select "select"select "select" ParameterType = "Product"FlushCache = "True"> 업데이트 제품 세트 이름 = #{name}, price = #{price} 여기서 id = #{id} 제한 1 </update> </mapper><cache type = "com.wooyoo.learning.util.rediscache"/>는 Redis 기반 보조 캐시를 활성화하고 업데이트 문에서 FlushCache를 true로 설정하여 제품 정보를 업데이트 할 때 캐시가 자동으로 무의미하게 표시 될 수 있습니다 (기본적으로 명확한 메소드가 호출).
시험
H2 메모리 데이터베이스를 구성하십시오
이 시점에서 우리는 모든 코드 개발을 완료했으며 다음은 코드의 품질을 테스트하기 위해 단위 테스트 코드를 작성해야합니다. 개발 프로세스에서 우리는 MySQL 데이터베이스를 사용했으며 일반적으로 테스트 중에 메모리 내 데이터베이스를 사용했습니다. 여기서는 테스트 시나리오에 사용 된 데이터베이스로 H2를 사용합니다.
H2를 사용하는 것도 매우 간단합니다. MySQL을 사용할 때만 구성하면됩니다. application.yml 파일에서 :
---- 스프링 : 프로파일 : 테스트 # 데이터베이스 구성 데이터 소스 : URL : JDBC : H2 : MEM : 테스트 사용자 이름 : 루트 비밀번호 : 123456 드라이버 클래스-이름 : org.h2. 드리버 스키마 : 클래스 경로 : schema.sql 데이터 : classpath.sql
기본 구성과의 충돌을 피하기 위해, 우리는 --- 새로운 단락을 시작하고 프로파일을 사용하기 위해 사용합니다. 테스트하여 테스트 환경의 구성임을 나타냅니다. 그런 다음 @ActiveProfiles (profiles = "test") 주석을 테스트 클래스에 추가하여 테스트 환경에서 구성을 활성화하여 MySQL 데이터베이스에서 한 번의 클릭으로 H2 데이터베이스로 전환 할 수 있습니다.
위 구성에서 schema.sql은 테이블 작성 문을 저장하는 데 사용되며 data.sql은 삽입 데이터를 저장하는 데 사용됩니다. 이러한 방식으로, 우리가 테스트 할 때, H2는이 두 파일을 읽고, 필요한 테이블 구조와 데이터를 초기화 한 다음 테스트가 끝날 때이를 파괴하여 MySQL 데이터베이스에 영향을 미치지 않습니다. 이것은 메모리 내 데이터베이스의 이점입니다. 또한 pom.xml에서 테스트하도록 H2 의존성 범위를 설정하는 것을 잊지 마십시오.
Spring Boot를 사용하면 간단하므로 코드를 수정하지 않고 다른 환경에서 데이터베이스를 쉽게 전환 할 수 있습니다.
테스트 코드 작성
Spring Initializer를 통해 초기화되었으므로 이미 테스트 클래스 -SpringBootmyBatisWithRedisApplicationTests가 있습니다.
Spring Boot는 TestRestTemplate과 같은 웹 인터페이스 테스트를 수행 할 수있는 몇 가지 도구 클래스를 제공합니다. 그런 다음 구성 파일에서 디버그 로그의 관찰을 용이하게하기 위해 로그 레벨을 디버그로 조정합니다. 특정 테스트 코드는 다음과 같습니다.
@runwith (springrunner.class) @springboottest (webenvironment = springboottest.webenvironment.random_port) @ActiveProfiles (profiles = "test") 공개 클래스 SpringBootmyBatisswithRedisApplicationTests {@LocalSerport 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); 제품 NewProduct = 신제품 (); Long NewPrice = New Random (). NextLong (); newProduct.setName ( "새 이름"); 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); }}위의 테스트 코드에서 :
먼저 Get 인터페이스를 호출하고 Assert 문을 사용하여 예상되는 객체가 얻어 졌는지 여부를 결정합니다. 현재 제품 객체는 Redis에 저장됩니다.
그런 다음 PUT 인터페이스를 호출하여 제품 객체를 업데이트하면 Redis 캐시가 무효화됩니다.
마지막으로, 우리는 Get 인터페이스를 다시 호출하여 새로운 제품 객체를 얻었는지 여부를 결정합니다. 이전 객체가 얻어지면 캐시 잘못된 코드가 실행되지 않았고 코드에 오류가 있음을 의미합니다. 그렇지 않으면 코드가 정상임을 의미합니다.
단위 테스트 작성은 좋은 프로그래밍 습관입니다. 비록 당신을 위해 일정 시간이 걸리지 만, 앞으로 리팩토링 작업을해야 할 때 과거에 단위 테스트를 한 스스로에게 감사 할 것입니다.
테스트 결과를 봅니다
Intellij에서 테스트 사례를 실행하려면 클릭하면 테스트 결과는 다음과 같습니다.
녹색이 표시되어 테스트 사례가 성공적으로 실행되었음을 나타냅니다.
요약
이 기사는 Spring Boot, Mybatis 및 Redis를 사용하여 최신 웹 프로젝트를 신속하게 구축하는 방법을 소개하고 Spring Boot에서 단위 테스트를 우아하게 작성하여 코드의 품질을 보장하는 방법을 소개합니다. 물론이 프로젝트에는 또 다른 문제가 있습니다. 즉, Mybatis의 레벨 2 캐시는 전체 DB를 플러시하여 무효화 할 수 있습니다. 현재 무효화 할 필요가없는 일부 캐시도 무효화 될 수 있으므로 특정 제한 사항이 있습니다.