
하…JDBC 진짜 어렵네
이거를 직접 사용하신 백엔드 조상님들은 참 대단하신거 같다 ..ㅎ
connection이 자동문이었으면 얼마나 좋을까
알아서 문 좀 닫고 나가지ㅜ
그래도 MyBatis는 처음 해봤는데 생각보다 재밌는것 같다!
단짠단짠 수업 🤯 😊그리고 오늘 또!! 인터넷 연결이 말썽이어서 몇번 들어왔다 나갔다.
다시 들어가면 강사님과 FT님만 계시는 대강당을 지나가야 되는데 그럴때마다 굉장히 머쓱,,,ㅎㅎ
@Override
public Member save(Member member) throws SQLException {
String sql = "insert into member (username, password) values (?, ?)";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getUsername());
pstmt.setString(2, member.getPassword());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if ( rs.next() ) {
int idx = rs.getInt(1);
member.setMemberId(idx);
}
return member;
} catch ( SQLException e ) {
throw e;
} finally {
closeConnection(conn, pstmt, rs);
}
}
Statement.RETURN_GENERATED_KEYS → 생성된 primary키를 받아올 수 있음@Override
public Optional<Member> findById(Integer id) throws SQLException {
String sql = "select * from member where member_id = ?";
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, id);
resultSet = preparedStatement.executeQuery();
if ( resultSet.next() ) {
Member findMember = new Member(
resultSet.getInt("member_id"),
resultSet.getString("username"),
resultSet.getString("password")
);
return Optional.of(findMember);
} else {
return Optional.empty();
}
} catch ( SQLException e ) {
throw e;
} finally {
closeConnection(connection, preparedStatement, resultSet);
}
}
@Override
public void update(Member member) {
String sql = "update member set password = ? where member_id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member.getPassword());
pstmt.setInt(2, member.getMemberId());
pstmt.executeUpdate();
}catch (SQLException e){
}finally {
closeConnection(conn, pstmt, null);
}
}
Service에 @Transactional을 붙이는 이유
public void logic() throws Exception {
Member saveReq = new Member;
Member saved = repository.save(saveReq);
saved.setPassword("asdf");
// 만약 에러가 발생한다면?
repository.update(saved);
}
트랜잭션은 커넥션 단위로 이루어진다.
save 와 update가 repository에서 커넥션 연결이 다르기 때문에, update가 실행되기 전에 에러가 발생된다면 한 로직 안에서 처리되어야 하는 update가 반영되지 않는 문제가 생김
이럴 땐 service에서 트랜잭션을 관리할 수 있도록 설정해준다.
public void logic1(Member saveReq, boolean isRollback) throws Exception {
TransactionStatus transaction = transactionManager.getTransaction(
new DefaultTransactionDefinition()
//기본적인 설정의 트랜잭션을 만들어줌
);
//트랜잭션을 서비스에서 관리할 수 있도록
try{
Member saved = repository.save(saveReq);
Optional<Member> memberOptional = repository.findById(saved.getMemberId());
Member findMember = memberOptional.orElseThrow();
log.info(findMember.getUsername());
if(isRollback){
transactionManager.rollback(transaction);
return;
}
transactionManager.commit(transaction);
}catch (Exception e){
transactionManager.rollback(transaction);
}
}
transactionManager → 스프링이 제공하는 트랜잭션 관리 객체이고, DB와의 커넥션을 열고 닫고, 커밋하거나 롤백하는 일을 담당getTransaction(new DefaultTransactionDefinition()) → new DefaultTransactionDefinition() 을 통해 트랜잭션의 동작방식을 정의하고 트랜잭션을 시작함SQL Mapper
의존성 설정 -> DB 설정 -> MyBatis 설정 -> Mapper 인터페이스 작성 -> XML 작성 -> MyBatis 사용
@Mapper
public interface ItemMapper {
void save(Items items);
void update(@Param("price") Integer price, @Param("id") Long id);
Items findByItemCode(@Param("itemCode") String itemCode);
void remove(Items items);
}
mapper → 연결할 Mapper파일의 경로
<insert>
<select>
<update>
<delete>
<insert>
→ id안에 mapper의 메서드를 넣어줘야함
→ 해당 메서드가 실행될 때 태그안에 있는 쿼리문이 실행
void save(String name, String itemCode, Integer price);
<insert id="save" useGeneratedKeys="true" keyProperty="id">
INSERT INTO items (name, code, price)
values (#{name}, #{itemCode}, #{price})
</insert>
<update id="update">
UPDATE items
SET price = #{price}
WHERE item_id = #{id}
</update>
<select>
첫번째 커서가 가리키고 있는 이름을 객체의 이름과 맞춰주면 된다
<select id="findByItem">
SELECT i.item_id as id,
i.name,
i.code as itemCode,
i.price,
i.created_at as createdAt
FROM items i
WHERE i.code = #{itemCode}
</select>
type을 찾을 수 있는 파일의 경로를 지정
<select id="findByItem" resultType="io.shs0160.dao.global.entity.Items">
SELECT i.item_id as id,
i.name,
i.code as itemCode,
i.price,
i.created_at as createdAt
FROM items i
WHERE i.code = #{itemCode}
</select>
<delete id="remove">
DELETE FROM items i
WHERE
i.item_id = #{id}
</delete>
mybatis:
mapper-locations: → mapper.xml의 위치type-aliases-package → xml안에 있는 타입들을 찾을 수 있는 경로 지정복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식
this 는 Builder 객체 자신을 말함 public static class Builder {
private String name;
private Integer age;
private String email;
private String address;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
}