만들 부분은 Service이며
만들어야할 구성요소는 2가지.
레이어 | 구성요소 | 명칭 | 비고 |
---|---|---|---|
도메인 계층 | Service | AccountService | 인터페이스 생성 |
도메인 계층 | ServiceImpl | AccountServiceImpl | 서비스 구현 |
서비스
는 하나의 인터페이스다.
여기서 작성하는 인터페이스는 메소드의 구체적인 처리내요을 기술하지 않는 추상적메소드
로 작성한다.
src/main/java/service
패키지를 추가한 후, 이 경로에 AccountService
인터페이스를 추가함.
AccountService.java
package com.example.account.service;
import com.example.account.entity.Account;
import java.util.Optional;
public interface AccountService {
// 회원정보 모든 내용 취득
Iterable<Account> selectAll();
// 회원정보를 id를 키로 1건 취득
Optional<Account> selectOneById();
//회원정보등록
void insertAccount(Account account);
//회원정보 업데이트
void updateAccount(Account account);
//회원정보 삭제
void deleteAccountById(Integer id);
}
전부 CRUD 처리를 취급하는 메소드들로 구성되어 있음.
앞서 만든 인터페이스를 구현하여 AccountServiceImpl
을 생성함.
같은 src/main/java/service
패키지 안에 구현
AccountServiceImpl.java
package com.example.account.service;
import com.example.account.entity.Account;
import com.example.account.repository.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class AccountServiceImpl implements AccountService {
// Repository 주입
@Autowired
AccountRepository repository;
@Override
public Iterable<Account> selectAll() {
return repository.findAll();
}
@Override
public Optional<Account> selectOneById(Integer id) {
return repository.findById(id);
}
@Override
public void insertAccount(Account account) {
repository.save(account);
}
@Override
public void updateAccount(Account account) {
repository.save(account);
}
@Override
public void deleteAccountById(Integer id) {
repository.deleteById(id);
}
}
@Service
어노테이션을 클래스에 부여하여 인스턴스 생성 대상으로 지정함.
@Autowired
어노테이션으로 AccountRepository
를 주입함.
서비스 처리를 하면서 DB 이용시 주의해야할 부분이 바로 트랜잭션 관리.
Spring Framework가 제공하는 AOP기능 중 하나인 @Transactional 어노테이션에 대한 사용법을 기반으로 설명.
트랜잭션은 데이터베이스에서 하나의 논리적 기능을 수행하는 연산자들의 집합을 의미함.
트랜잭션의 특징은 성공과 실패 중 하나만이 존재한다는 점인데, 복수의 처리 중 하나라도 실패한다면 트랜잭션은 트랜잭션의 실행 전으로 돌아가는데 이를 롤백(ROLL BACK)
이라고 함
반대로 모든 처리가 성공했다면 처리가 확정되며, 이를 커밋(COMMIT)
이라고 함.
따라서 이러한 트랜잭션은 시작과 끝을 명시적으로 지정해야 하며, 시작부터 끝까지의 범위를 트랜잭션 범위라고 한다.
Spring에서 제공하는 기본전략에서는 범위를 Service 에서 Repository로 가는 사이로 한정짓는다. 따라서 이러한 전략을 가지고 트랜잭션 범위는 Service로 지정한다.
Spring에서 제공하는 @Transactional
어노테이션을 사용한다.
클래스나 메소드에 부여하면 자동으로 트랜잭션의 시작, 커밋, 롤백 등이 자동으로 처리가 되면서 관리가 된다.
권장하는 방법은 클래스에 부여하는 방법으로, 설정버그를 방지하는 목적이다.
따라서 AccountServiceImpl
에 어노테이션을 추가해준다
AccountServiceImpl.java
package com.example.account.service;
import com.example.account.entity.Account;
import com.example.account.repository.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
@Transactional
public class AccountServiceImpl implements AccountService {
...
이러한 어노테이션을 부여하면서, 여러가지 명령으로 트랜잭션을 직접 관리하지 않고, Spring Framework가 알아서 담당을 해주게 된다.
우선 DB처리 테스트를 했을 때 썼던 데이터를 지우기 위해 accountdb의 account 테이블을 초기화 시켜줌
MySQL Qurey문에 해당 쿼리문을 입력.
DELETE FROM account;
ALTER TABLE account AUTO_INCREMENT = 1;
데이터를 모두 삭제하고 AUTO_INCREMENT의 일련번호의 1로 초기화함.
AccountApplication.java
package com.example.account;
import com.example.account.entity.Account;
import com.example.account.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.*;
@SpringBootApplication
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args).getBean(AccountApplication.class).execute();
}
@Autowired
AccountService service;
/**
* 실행 메소드
*/
private void execute() {
// 등록처리
//setup();
//showList();
//showOne();
//updateAccount();
///deleteAcoount();
}
/**
* 2개의 계정 추가
*/
private void setup() {
System.out.println("--- 등록시작 ---");
// 엔티티 생성
Account account1 = new Account(null, "aaaa", "가나다","aaaa@gmail.com","11111");
Account account2 = new Account(null, "bbbb", "James","james@naver.com","22222");
Account account3 = new Account(null, "cccc", "Jonh","jonh@naver.com","22322");
Account account4 = new Account(null, "de123f", "김현철","kim@gmail.com","2udef12");
Account account5 = new Account(null, "iamkanye", "Kanye","kanyewest@gmail.com","2udsrgqw2");
//목록에 엔티티 저장
List<Account> accountList = new ArrayList<>();
// 첫 번째 인수는 저장대상이고 두번째 인수는 가변길이 인수.
Collections.addAll(accountList, account1, account2, account3, account4, account5);
for (Account account: accountList) {
// 등록실행
service.insertAccount(account);
}
System.out.println("--- 등록 종료 ---");
}
/**
* 전체 검색 얻기
*/
private void showList() {
System.out.println("---전체 검색 시작---");
// Repository를 사용하여 모든 내역을 얻는 결과
Iterable<Account> accounts = service.selectAll();
for (Account account : accounts) {
System.out.println(account);
}
System.out.println("---전체 검색 종료 ---");
}
/**
* 1건 데이터 얻기
*/
private void showOne() {
System.out.println("---1건의 데이터 얻기---");
// Repository를 사용하여 1건의 데이터를 얻고 결과를 얻음
// return값은 Optional
Optional<Account> accountOptional =service.selectOneById(5);
// 값 존재 확인
if(accountOptional.isPresent()) {
System.out.println(accountOptional.get());
} else {
System.out.println("해당 퀴즈가 없습니다.");
}
System.out.println("---1건 데이터 얻기 완료---");
}
/**
* 업데이트 처리
*/
private void updateAccount() {
System.out.println("--- 업데이트 처리 시작 ---");
// 변경 엔티티 설정
Account account1 = new Account(1, "abab","가가가", "abab@gmail.com", "1212");
// 업데이트 실행
service.updateAccount(account1);
// 업데이트 확인
System.out.println("--- 업데이트 처리 완료 ---");
}
/**
* 삭제
*/
private void deleteAcoount() {
System.out.println("--- 삭제 처리 실행 ---");
service.deleteAccountById(2);
System.out.println("--- 삭제 처리 완료 ---");
}
}
전 편에서 실행했던 것 같이 execute 메소드에 다양한 값을 넣으면서 실행이 되는지 테스트.
잘 실행이 되었다면 이제 AccountApplication.java
는 사용할 일이 없으니 비워주도록 하자.
AccountApplication.java
import org.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AccountApplication {
SpringApplication.run(AccountApplication.class, args).getBean(AccountApplication.class).execute();
}