#트랜잭션 설명
##1.트랜잭션의 의미
> 일련의 작업 단위
> 완전히 처리되거나 All-OR-Nothing
##2.소프트웨어에서의 트랜잭션 처리
> 기본적으로(전통적으로) 하나의 함수에 묶어서 처리함.
> 스프링의 경우 그 기준은 비지니스 로직을 처리하는 서비스 단에서의 함수로 묶어버림
###★1)방법 사용
###1)controller(view결정) 함수 1개 끌고옴 << service에서 (비지니스 로직 처리/기능) 댓글달기 << mapper에서 함수 2개 끌고옴
###2)controller 함수 2개 끌고옴 << service : 함수 2개 생성(step update/댓글 insert함수)
##3.스프링에서는 기본적으로 함수, 클래스, 인터페이스 위에 @Transactional로 롤백처리
> 그럼 롤백(트랜잭션은) 언제 일어나는가? → 해당 함수에서 에러, Exception(예외)가 일어났을때
##4.@Transactional과 Checked(정적회로)/UnChecked(동적회로) Exception과의 관계
> 결론
> 예외에 따른 롤백처리는 Checked 예외는 롤백되지 않고, Unchecked 예외는 롤백됩니다.
> Checked Exception 일때는 Rollback을 하지 않는다.
> @Transactional(rollbackFor=Exception, class) 옵션으로 모든 예외에 대해서 롤백할 수 있다.
=================================================================================
> 기본적으로 @Transactional 은 UnChecked Exception과 error 객체 대상으로 롤백시킴
> Checked Exception도 롤백 시키고 싶으면, rollbackFor = Exception.class를 쓰면 됨.
> 개발자가 컨트롤할 수 있는(try catch) 오류사항들에 대해 객체로 만들어놓았다.
> thorws Checked Exception이 적혀있으면 무조건 try & catch
int a = 10/0; → JVM이 프로그램 실행을 중단 시키고 에러 메세지를 출력한다.
컴파일 할 때 에러 처리를 요구
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine(); → try catch를 사용하지 않으면 에러발생
try{br.readLine();} 해야함
Checked Exception - 반드시 try&catch문 사용
Unchecked Exceptions - 실시간 에러
ArithmeticException : 예외적인 산술 조건이 발생 ex)0으로 나누기
ClassCastException : 하위 유형이 아닌 유형에 대한 참조를 캐스팅하려고 시도함
NullPointerException : 객체 안에 Null값이 들어있는데 객체 호출할 때
ArrayINdexOutOfBoundsException : 인덱스가 배열의 크기보다 크거나 음수 인덱스 요청
ArrayStoreException : 선언된 유형 대신 배열에 다른 유형의 객체를 저장하려고 할 때 발생
[database]
create table users(
username varchar2(50) not null primary key,
password varchar2(100) not null,
enabled char(1) DEFAULT '1'
);
create table authorities (
username varchar2(50) not null,
authority varchar2(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
commit;
insert into users (username,password) values('user','user');
insert into users (username,password) values('member','member');
insert into users (username,password) values('admin','admin');
commit;
insert into AUTHORITIES (username,AUTHORITY) values('user','ROLE_USER');
insert into AUTHORITIES (username,AUTHORITY) values('member','ROLE_MANAGER');
insert into AUTHORITIES (username,AUTHORITY) values('admin','ROLE_MANAGER');
insert into AUTHORITIES (username,AUTHORITY) values('admin','ROLE_ADMIN');
commit;
[계산 UserMapper.java] package edu.global.ex.mapper; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import edu.global.ex.vo.UserVO; @Mapper // MyBatis용 인터페이스라는 것을 알려주는 어노테이션 //SecurityConfig.java의 Bean을 끌고옴 public interface UserMapper { public UserVO getUser(String username); @Insert("insert into users(username,password,enabled) values(#{username},#{password},#{enabled})") public int insertUser(UserVO userVO); @Insert("insert into AUTHORITIES (username,AUTHORITY) values(#{username},'ROLE_USER')") public void insertAuthorities(UserVO UserVO); @Insert("insert into AUTHORITIES (username,AUTHORITY) values(#{username},'ROLE_ADMIN')") public void insertAdminAuthorities(UserVO UserVO); } --------------------------------------------------------------------- [계산 UserService.properties] package edu.global.ex.service; import java.sql.SQLException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import edu.global.ex.mapper.UserMapper; import edu.global.ex.vo.UserVO; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class UserService { @Autowired private UserMapper userMapper; // 1.정상 동작 코드 => User를 insert를 넣은후 권한 까지 insert 해야 됨 => // 2개가 정상적인 동작이 되어야 됨(트랜잭션단위) public void addUser(UserVO user) { log.info("addUser().."); // 반드시 같이 이루어져야하는 작업단위이므로 Service에서 함수로 묶음(트랜잭션) userMapper.insertUser(user); // User를 insert를 넣은후 userMapper.insertAuthorities(user); // 권한 설정 } // 2.아래는 select * from users; 에는 들어가 있으나 // select * from authorities 권한에는 안 들어가 있음 public void addUser2(UserVO user) { log.info("addUser2().."); userMapper.insertUser(user); // User를 insert를 넣은후 user = null; // 일부러 에러냄 userMapper.insertAuthorities(user); // 권한 설정 } // 3. 아래처럼 @Transactional을 붙임 롤백되어 아무런 데이터가 들어가지않음 @Transactional // insertUser가 안된 상태로 되돌려라 public void addUser3(UserVO user) { log.info("addUser3().."); userMapper.insertUser(user); // User를 insert를 넣은후 user = null; // 일부러 에러냄 userMapper.insertAuthorities(user); // 권한 설정 } // 4.checked Exception을 강제로 시켜 봄 //롤백 되지 않음 @Transactional public void addUser4(UserVO user) throws Exception { log.info("addUser().."); userMapper.insertUser(user); // User를 insert를 넣은후 userMapper.insertAuthorities(user); // throw Checked Exception throw new Exception("Exception (Checked Exception)"); } // 5. unChecked Exception을 강제로 시켜 봄 // 롤백 됨 @Transactional public void addUser5(UserVO user) throws Exception { log.info("addUser5().."); userMapper.insertUser(user); // User를 insert를 넣은후 userMapper.insertAuthorities(user); // 해당 코드 때문에 롤백 됨 // throw unChecked Exception throw new RuntimeException("RuntimeException (unChecked Exception)"); } // ★추천 6. rollbackFor 옵션을 줌 // 롤백 됨 @Transactional(rollbackFor = Exception.class) public void addUser6(UserVO user) throws Exception { log.info("addUser().."); userMapper.insertUser(user); // User를 insert를 넣은후 userMapper.insertAuthorities(user); // throw Checked Exception throw new Exception("RuntimeException (Unchecked Exception)"); } } --------------------------------------------------------------------- [계산 LoginController.java] package edu.global.ex.controller; import java.security.Principal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import edu.global.ex.service.UserService; import edu.global.ex.vo.UserVO; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller public class LoginController { @Autowired private UserService userService; @Autowired private PasswordEncoder encoder; // SecurityConfig.java의 Bean bCryptPasswordEncoder를 꺼내옴 @GetMapping("/login") public String login() { log.info("login().."); return "login/login"; } @RequestMapping(value = "/loginInfo", method = RequestMethod.GET) public String loginInfo(Authentication authentication, Principal principal, Model model) { // session에 저장된 시큐리티 객체에서 불러온다.(UserDetails 기반으로 만들었음) // Controller함수에서만 받아올 수 있음 String user_id; // 1.SpringContextHolder를 통하여 가져오는 방법(일반적인 빈에서 사용 할수있음 ) Authentication auth = SecurityContextHolder.getContext().getAuthentication(); user_id = auth.getName(); System.out.println("유저 아이디:" + user_id); // 2.authentication 객체로 가져오는 방법(유저이름, 권한 가져올 수 있음) System.out.println("authentication 유저 아이디:" + authentication.getName()); System.out.println("authentication 권한들:" + authentication.getAuthorities()); // 3.Principal 객체로 가져오는 방법(가져올수 있는게 getName() 정도) System.out.println("Principal 유저 아이디:" + principal.getName()); return "redirect:/"; } // restful방식 /{id}/{pw} @GetMapping("/addUser/{id}/{pw}") // url을 파라미터로 받겠다. @PathVariable에 {id}와 {pw}값을 넘김 public String addUser(@PathVariable String id, @PathVariable String pw) throws Exception { System.out.println(id + ":" + pw); UserVO user = new UserVO(); user.setEnabled(1); user.setUsername(id); user.setPassword(encoder.encode(pw.toString().trim())); // encoder : 암호화 객체 System.out.println(user); // userService.addUser(user); // userService.addUser2(user); userService.addUser3(user); return "redirect:/"; } }
[결과값]
http://localhost:8282/addUser/kim6/kim6
에러발생 : There was an unexpected error (type=Internal Server Error, status=500).
Database에도 올라가지 않음.