Portable Service Abstraction
Service Abstraction ?
예제(PetClinic)에서는 아래와 같이 Servlet을 형성하지 않는다.
// /owner/create
public class OwnerCreateServlet extends HttpServlet {
// GET
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
// POST
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
예제(PetClicin)에서는 아래와 같이 형성한다.
main\java\org\owner\OwnerController
@GetMapping("/owners/new")
@LogExecutionTime
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner();
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/owners/new")
@LogExecutionTime
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
else {
this.owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
}
@GetMapping
과 @PostMapping
을 이용.
@
을 사용했지만 그 아래 기반에는 서블릿 기반의 코드가 동작한다. (추상화 계층 사용)
PSA 사용 이유 : 참고 사이트
@Controller
를 사용하면 요청을 맵핑(@RequestMapping
(@GetMapping
@PostMapping
등))
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
@GetMapping("/owners/new") //Controller
@LogExecutionTime
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner(); //Model
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; //View
}
/owners/new
와 같은 URL에 해당하는 요청이 들어왔을때, Method가 처리하도록 매핑(GetMapping)P(ortable)이 붙은 이유 : 여러 기술을 다른 것으로 변화 가능하다.
Spring에서 제공하는 추상화 인터페이스 기능을 쓰지만 (아래)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
(위) 의존성을 추가하면 톰캣이 아닌 네티와 같은 서버에서도 Spring 기반의 프로그래밍이 구현된다.
결론 : Servlet/Reactive 를 선택할 수 있으며, 톰캣/네티/언더로우 등 서버 또한 바꿀 수 있다. !코드를 거의 변경하지 않고!
트랜잭션 : 데이터의 하나의 작업 단위(처음부터 끝까지)
아래의 코드를 보자. (Low Level의 트랙잭션 처리)
import java.math.BigDecimal;
import java.sql.*;
import java.time.LocalDateTime;
public class TransactionExample {
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection(
"jdbc:postgresql://127.0.0.1:5432/test", "postgres", "password");
Statement statement = conn.createStatement();
PreparedStatement psInsert = conn.prepareStatement(SQL_INSERT);
PreparedStatement psUpdate = conn.prepareStatement(SQL_UPDATE)) {
statement.execute(SQL_TABLE_DROP);
statement.execute(SQL_TABLE_CREATE);
// start transaction block
conn.setAutoCommit(false); // default true
// Run list of insert commands
psInsert.setString(1, "mkyong");
psInsert.setBigDecimal(2, new BigDecimal(10));
psInsert.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now()));
psInsert.execute();
psInsert.setString(1, "kungfu");
psInsert.setBigDecimal(2, new BigDecimal(20));
psInsert.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now()));
psInsert.execute();
// Run list of update commands
// error, test roolback
// org.postgresql.util.PSQLException: No value specified for parameter 1.
psUpdate.setBigDecimal(2, new BigDecimal(999.99));
//psUpdate.setBigDecimal(1, new BigDecimal(999.99));
psUpdate.setString(2, "mkyong");
psUpdate.execute();
// end transaction block, commit changes
conn.commit();
// good practice to set it back to default true
conn.setAutoCommit(true);
} catch (Exception e) {
e.printStackTrace();
}
}
//...
}
conn.setAutoCommit(false);
: sql이 여러번 날아가더라도 커밋을 하지 않는다.conn.commit();
: ~~ 코드를 마치고 커밋.스프링의 @Transactional
: Annotation이 붙어 있는 Method는 따로 트랜잭션 처리를 하지 않아도 된다. 헉
@Cacheable
(@CacheEvit...
등) 를 활용하여 자신의 코드(메소드) 를 변경하지 않고 CacheManager를 적용할 수 있다.