Spring(2023-04-26)

권단비·2023년 4월 26일
0

IT

목록 보기
128/139

[트랜잭션]

#트랜잭션 설명
##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에도 올라가지 않음.


0개의 댓글