728x90

4.1에서 예외의 전환 이유는 보통 2가지라고 말했었다.

하나는 예외를 더 구체적이고 의미 있도록 바꿔주는 것이고, 다른 하나는 런타임 예외로 바꾸어 catch를 줄여주는 것이었다.

 

JdbcTemplate은 SQLException을 DataAccessException등으로 바꿔줬었다.

여기에서도 예외를 더 구체적으로 알 수 있게 했으며, 런타임 예외로 바꾸던 것이다.

 

JDBC의 한계

JDBC는 데이터베이스에 접근하여 데이터를 가져오고 수정할 수 있기에 자바의 가장 많이 사용되는 API 중 하나라고 한다.

 

//MSSQL
SELECT TOP 5 * FROM USERS;	

//MYSQL
SELECT * FROM USERS LIMIT 5;

 

하지만 이렇게 SQL 중에서도 Database마다 다른 문법이 존재한다. 그럼 우리가 만든 UserDao는 MySQL이라는 데이터베이스에 종속되어 버리며, 다른 데이터베이스로 전환은 거의 불가능해진다. 그리고 여기서 발생하는 SQLException의 에러 정보도 DB마다 다르다.

if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY)

이런 코드가 있다면 MySQL 전용코드가 되어버리기에 여기에서도 MySQL에 종속되는 것이다. 만약 데이터베이스가 MySQL에서 MSSQL로 바뀐다면 현재 사용하는 query도 사용하지 못하고, 에러코드도 제대로 동작하지 못하는 것이다.

 

그렇기에 SQLException은 예외가 발생한 경우 DB의 상태 정보를 가져올 수 있도록 한다. getSQLState() 메서드를 통해 상태정보를 가져올 수 있도록 한다. 이때는 DB별로 다른 에러 코드를 통합하기 위해, Open Group의 XOPEN SQL 스펙의 SQL 상태코드를 따르도록 한다고 한다.

 

DB 에러 코드 매핑을 통한 전환

사용하는 DB를 바꾸더라도, 에러코드를 바꾸지 않으려면 이런 에러코드를 원하는 에러로 가져올 수 있도록 해야한다. 그렇기에 DB별 에러 코드를 참고해서 원하는 예외로 바꾸어야 하는 것이다.

 

오라클에서 PK 중복으로 발생하는 에러코드는 1이다.

이 에러코드를 duplicateKeyCodes로 변경해야 하는 것이다.

 

그렇기에 이런 에러 코드를 스프링의 예외 클래스로 매핑할 수 있도록 xml 파일을 작성할 수 있다.

<bean id="Oracle" class="org.springframework.jdbc.support.SQLErrorCodes">
	<property name="duplicateKeyCodes">
    	<value>1</value>
    </property>
</bean>

 

이렇게 작성해서 매핑한다.

 

JDBCTemplate 자체가 다 런타임 예외라서 예외를 많이 신경쓰지 않지만, 그럼에도 처리하고 싶은 예외가 있다면 이렇게 예외를 가져와서 처리할 수 있는 것이다.

 

DAO 인터페이스와 DataAccessException 계층구조

이렇게 Dao를 만들어서 사용하는 이유가 뭘까?

데이터베이스에 접근하는 로직을 분리하기 위해서다. 데이터 액세스 기술을 신경쓰지 않고, 그냥 단순히 Dao를 가져가서 사용하도록 만드는 것이다.

그렇기에 UserDao 인터페이스를 만들고, DI를 통해 주입받아서 사용하며 클라이언트에게 감추며 만드는 것이다.

public interface UserDao{
	public void add(User user);
}

 

이렇게 인터페이스를 만들 수 있을까?

지금은 JDBC를 사용하기에 별다른 예외가 발생하지 않아 가능하지만, 위 인터페이스는 구현 메서드에서 에러가 발생하기라도 하면 사용할 수 없는 인터페이스이다.

데이터 엑세스 기술에 따라 각각 다른 예외를 던지기 때문이다. 그렇다고 해서 모두를 포괄하는 Exception을 throws 할 수도 없을 것이다.

 

그렇기에 위의 인터페이스를 사용하려면 구현 메서드에서 체크 예외가 아닌 모두 런타임 예외를 사용해야 한다. 만약 체크 예외가 발생한다면 런타임 예외로 전환해주어야 할 것이다.

 

그럼 이제 add(User user) 메서드를 사용하면 체크 예외를 처리할 필요가 없을텐데, 그러면 모든 예외를 무시해도 되는가?

당연히 아닐 것이다.

JdbcTemplate은 에러가 발생하면 그에 맞는 DataAccessException의 서브 클래스로 바꿔준다. 그렇기에 그 계층 구조를 파악하고 있다가 처리 가능한 예외는 시도해 볼 수 있는 것이다.

 

DataAccessException의 서브 클래스들을 어느정도는 파악하고 있다가, 처리 가능한 예외는 처리해보도록 하자.

 

기술에 독립적인 UserDao 만들기

지금까지 개발했던 UserDao를 인터페이스랑 분리래보자.(사실 원래 이렇게 했어야 하는데...)

public interface UserDao {

    void add(User user);
    User get(String id);
    List<User> getAll();
    void deleteAll();
    int getCount();
}

 

public class UserDaoImpl implements UserDao {
}

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource"
          class = "org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="1204"/>
    </bean>

    <bean id="jdbcContext"
          class = "seungkyu.JdbcContext">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="userDao" class="seungkyu.UserDaoImpl">
        <constructor-arg ref="dataSource" />
    </bean>

</beans>
    @BeforeEach
    public void setUp() {
        this.userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.deleteAll();
    }

 

마지막 코드는 테스트 클래스의 일부인데, 여기서도 UserDaoImpl이 아닌 UserDao로 가져오는 것을 볼 수 있다.

빈의 이름은 인터페이스로 만들고, 그 구현체를 등록하는 방법을 사용해야 의존성이 낮아지기 때문이다.

이렇게 의존하고 있어야 나중에 편하게 구현체를 변경 가능하다.

 

그대로 테스트를 수행해보니

성공적으로 통과하는 것을 볼 수 있다.

 

이렇게 JDBC에서 발생하는 에러도 꼼꼼하게 테스트하면서 개발 할 수 있도록 하자.

728x90

이번엔 Map을 mapping 해본다.

 

이런 테이블이 있다고 해보자.

import jakarta.persistence.*;

import java.util.HashMap;
import java.util.Map;

@Entity
@Table(name = "book")
public class book {
    @Id
    private String id;
    private String title;
    private String content;
    @ElementCollection
    @CollectionTable(
            name = "book_prop",
            joinColumns = @JoinColumn(name="book_id")
    )
    @MapKeyColumn(name = "name")
    @Column(name = "value")
    private Map<String, String> props = new HashMap<>();

    protected book(){}

    public book(String id, String title, String content, Map<String, String> props) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.props = props;
    }
    
    //getter... setter...
}

이렇게 @CollectionTable로 저장할 테이블을 지정을 해주고

Map에 들어간 데이터를 어떤 column에 넣어줄지 PK는 @MapKeyColumn에, 그냥 데이터는 @Column으로 지정을 해준다.

 

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

import java.util.HashMap;
import java.util.Map;

public class book_main {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        Map<String, String> props1 = new HashMap<>();
        Map<String, String> props2 = new HashMap<>();

        props1.put("name1", "value1");
        props1.put("name2", "value2");
        book book1 = new book("1", "이름", "내용", props1);
        book book2;

        try{
            entityTransaction.begin();
            entityManager.persist(book1);
            entityTransaction.commit();
        }catch(NullPointerException exception){
            throw new NullPointerException();
        }

        try{
            book2 = entityManager.find(book.class, "1");
        }catch(NullPointerException exception){
            throw new NullPointerException();
        }

        System.out.println("book: " + book2);
        System.out.println("props: " + book2.getProps());
    }
}

이렇게 추가를 하고 조회를 하면

정상 작동하는 것을 볼 수 있다.

 

늘 그렇듯이 이번엔 Embeddable 타입이다.

이런 테이블이 있다고 하고, 여기서 PropValue를 Embeddable로 만들 생각이다.

우선 Embeddable 타입을 먼저 작성한다.

import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Embeddable;

@Embeddable
@Access(AccessType.FIELD)
public class PropValue {
    private String value;
    private boolean enabled;

    public PropValue(String value, boolean enabled) {
        this.value = value;
        this.enabled = enabled;
    }

    protected PropValue () {}
	
    //getter...setter...
    
    @Override
    public String toString() {
        return "PropValue{" +
                "value='" + value + '\'' +
                ", enabled=" + enabled +
                '}';
    }
}

이렇게 항상 작성하던 @Embeddable을 작성하고

import jakarta.persistence.*;

import java.util.HashMap;
import java.util.Map;

@Entity
@Table(name = "book")
public class book2 {
    @Id
    private String id;
    private String title;
    private String content;
    @ElementCollection
    @CollectionTable(
            name = "book_prop",
            joinColumns = @JoinColumn(name = "book_id")
    )
    @MapKeyColumn(name = "name")
    private Map<String, PropValue> props = new HashMap<>();

	//getter...setter...
    
    @Override
    public String toString() {
        return "book2{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", props=" + props +
                '}';
    }
}

@MapKeyColumn을 이용해서 그랬던 것처럼 작성을 해준다.

여기에 Map의 타입에 <PK, @Embeddable>을 넣어서 작성해준다.

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

import java.util.HashMap;
import java.util.Map;

public class book_main2 {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        Map<String, PropValue> Propvalues = new HashMap<>();

        PropValue propValue1 = new PropValue("value1", true);
        PropValue propValue2 = new PropValue("value2", true);

        Propvalues.put("String1", propValue1);
        Propvalues.put("String2", propValue2);

        book2 book1 = new book2("id1", "title1", "content1", Propvalues);
        book2 book2;

        try{
            entityTransaction.begin();
            entityManager.persist(book1);
            entityTransaction.commit();
        }catch (NullPointerException exception){
            throw new NullPointerException();
        }

        try{
            book2 = entityManager.find(book2.class, "id1");
            System.out.println(book2);
        }catch(NullPointerException exception){
            throw new NullPointerException();
        }finally {
            entityManager.close();
        }

        entityManagerFactory.close();

    }
}

이렇게 동작하는 것을 볼 수 있다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 10장 (영속 컨텍스트와 LifeCycle)  (0) 2023.03.22
JPA 8장 (List collection mapping)  (0) 2023.03.18
JPA 7장 (Set collection mapping)  (0) 2023.03.18
JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
728x90

이번엔 List이다.

문제에 관련된 DB가 있다고 하자.

테이블은

question
id varchar(10)
text varchar(20)
question_choice
question_id varchar(10)
idx integer(10)
text varchar(20)

그러면 question안에 객관식 보기인 question_choice들이 순서대로 들어가 있어야 할 것이다.

이런 경우에는 순서가 존재하기 때문에 저번에 사용한 Set을 사용할 수 없고, List를 사용해야 한다.

import jakarta.persistence.*;

import java.util.List;

@Entity
@Table(name = "question")
public class question {
    @Id
    private String id;
    private String text;

    @ElementCollection
    @CollectionTable(
            name = "question_choice",
            joinColumns = @JoinColumn(name = "question_id")
    )
    @OrderColumn(name = "idx")
    @Column(name = "text")
    private List<String> choices;

    protected question () {}

    public question(String id, String text, List<String> choices) {
        this.id = id;
        this.text = text;
        this.choices = choices;
    }
    //getter... settter
}
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

import java.util.List;

public class questionMain {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            question question1 = new question("1", "문제1", List.of("1번", "2번", "3번", "4번", "5번"));
            entityManager.persist(question1);
            entityTransaction.commit();
        }catch (Exception ignored){

        }

        try{
            question question2 = entityManager.find(question.class,"1");
            for(String choice : question2.getChoices()){
                System.out.println(choice);
            }
        }finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}

이렇게 Set과 똑같이 작성하지만, @OrderColumn을 추가해준다.

당연히 @Embedded도 사용이 가능하다.

import jakarta.persistence.Embeddable;

@Embeddable
public class choice {
    private String text;
    private boolean input;

    protected choice() {}

    public choice(String text, boolean input) {
        this.text = text;
        this.input = input;
    }

	//getter... setter...
}
import jakarta.persistence.*;

import java.util.List;

@Entity
@Table(name = "question")
public class question2 {
    @Id
    private String id;
    private String text;

    @ElementCollection
    @CollectionTable(
            name = "question_choice",
            joinColumns = @JoinColumn(name = "question_id")
    )
    @OrderColumn(name = "idx")
    private List<choice> choiceList;

    protected question2 (){}

    public question2(String id, String text, List<choice> choiceList) {
        this.id = id;
        this.text = text;
        this.choiceList = choiceList;
    }
	//getter... setter...
}
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

import java.util.List;

public class questionMain2 {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            question2 question1 = new question2("1", "문제1", List.of(new choice("1", false),
                    new choice("2", true)));
            entityManager.persist(question1);
            entityTransaction.commit();
        }catch (Exception ignored){

        }

        try{
            question2 question2 = entityManager.find(question2.class,"1");
            for(choice choice : question2.getChoiceList()){
                System.out.println(choice.getInput() + ", " + choice.getText());
            }
        }finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}

'백엔드 > JPA' 카테고리의 다른 글

JPA 10장 (영속 컨텍스트와 LifeCycle)  (0) 2023.03.22
JPA 9장 (Map collection mapping)  (0) 2023.03.21
JPA 7장 (Set collection mapping)  (0) 2023.03.18
JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
728x90

 

이런 테이블들이 있다고 해보자.

job
id VARCHAR(10)
name VARCHAR(100)

 

job_perm
job_id VARCHAR(10)
permission VARCHAR(20)

 

이렇게 job 안에 permission에 해당 하는 값들을 보관하고 있어야 한다.

import jakarta.persistence.*;

import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "job")
public class job {
    @Id
    private String id;
    private String name;

    @ElementCollection
    @CollectionTable(
            name="job_perm", //job_perm 테이블 명
            joinColumns = @JoinColumn(name = "job_id") //job_perm 테이블의 job_id column
    )
    @Column(name = "perm") //job_perm 테이블의 permission column
    private Set<String> permissions = new HashSet<>();

    protected job() {}

    public job(String id, String name, Set<String> permissions) {
        this.id = id;
        this.name = name;
        this.permissions = permissions;
    }
	//getter... setter...
    public Set<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }
}

이렇게 내부에 Set을 만들고 @ElementCollection을 작성해준다.

 

한 번에 이해하기 힘들테니 바로 실행을 해보고 결과를 확인해보자.

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

import java.util.Set;

public class roleMain{
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            job job1 = new job("1", "안녕", Set.of("11", "22"));
            entityManager.persist(job1);
            entityTransaction.commit();
        }catch(Exception ignored){

        }

        try{
            job job2 = entityManager.find(job.class, "1");
            for(String perm : job2.getPermissions()){
                System.out.println(perm);
            }
        }finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}

job을 persist 할 때 Set에 11, 22에 해당하는 값을 입력해주면

이렇게 job 테이블에 데이터 하나가 추가가 되고

job_perm 테이블에는 집합으로 넣었던 데이터 2개가 추가가 된다.

find로 조회할 때는

class에 작성했던 getPermission을 통해 for-each문으로 가져오면 된다.

 

당연히 Embeddable 타입도 가능하다.

이런 관계에서도 위와 비슷하게 @Embeddable 클래스만 만들어주고 동일 작업을 수행하면 된다.

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;

@Embeddable
public class information {
    @Column(name = "perm")
    private String permission;
    private String info;

    protected information () {}

    public information(String permission, String info) {
        this.permission = permission;
        this.info = info;
    }
    
    //getter... setter
}
import jakarta.persistence.*;

import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name="role")
public class job2 {
    @Id
    private String id;
    private String name;

    @ElementCollection
    @CollectionTable(
            name="role_perm",
            joinColumns = @JoinColumn(name = "role_id")
    )
    private Set<information> informationSet = new HashSet<>();

    protected job2(){}

    public job2(String id, String name, Set<information> informationSet) {
        this.id = id;
        this.name = name;
        this.informationSet = informationSet;
    }
	//getter... setter...
    public Set<information> getInformationSet() {
        return informationSet;
    }

    public void setInformationSet(Set<information> informationSet) {
        this.informationSet = informationSet;
    }
}

이렇게 정상적으로 작동하게 된다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 9장 (Map collection mapping)  (0) 2023.03.21
JPA 8장 (List collection mapping)  (0) 2023.03.18
JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
728x90

@Embeddable annotation에 대해 배워보자.

 

테이블에 값을 저장할 때 

title date category publisher writer_name writer_mail
... ... ... ... ... ...

writer_name, witer_mail 같이 서로 묶이면 더 보기 편할 거 같은 column들이 있다.

이럴 때 @Embeddable을 사용한다.

@Embeddable은 엔티티가 아닌 타입을 한 개 이상의 필드와 매핑할 때 사용한다.

 

package user;

import jakarta.persistence.Embeddable;

@Embeddable
public class school {

    private String elementarySchool;
    private String middleSchool;
    private String highSchool;

    public school(String elementarySchool, String middleSchool, String highSchool) {
        this.elementarySchool = elementarySchool;
        this.middleSchool = middleSchool;
        this.highSchool = highSchool;
    }

    protected school() {

    }

	//getter..., setter...
}

예시를 보자.

학교와 관련된 column들을 school이라는 클래스에 묶었다.

당연히 여기서 변수 명들은 각자의 column명과 일치하거나, 일치하지 않는다면 @Column으로 mapping을 해주어야 한다.

그리고 school이라는 클래스에 @Embeddable을 달아준다.

 

이제 테이블에 해당하는 클래스를 작성하자.

package user;

import jakarta.persistence.*;

@Entity
@Table(name = "user")
public class user {

    @Id
    private String email;
    private String name;
    @Embedded
    private school school;

    protected user() {
    }

    public user(String email, String name, school school) {
        this.email = email;
        this.name = name;
        this.school = school;
    }

	//getter... setter...
}

그러고 Id에 해당하는 멤버에 @Embedded를 달아준다.

이렇게 정상적으로 생성과 조회가 되는 것을 볼 수 있다.

 

만약 여기 school에 null을 넣어준다면, school에 들어있는 column의 값들에도 모두 null이 들어가게 된다.

 

같은 @Embeddable 타입 필드가 두 개라면 어떻게 될까?

당연히 에러가 난다.

 

그럴 때는 뒤에 오는 column은 @AttributeOverride로 설정을 재정의 해줘야 한다.

package school;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;

@Embeddable
public class info {

    @Column(name = "eleaddr")
    private String addr;
    @Column(name = "elenum")
    private String num;

    protected info(){}

    public info(String addr, String num) {
        this.addr = addr;
        this.num = num;
    }
    //getter... setter...
}
package school;

import jakarta.persistence.*;

@Entity
@Table(name = "school_table")
public class school {

    @Id
    @Embedded
    private info elementInfo;

    @AttributeOverrides({
            @AttributeOverride(name = "addr", column = @Column(name = "midaddr")),
            @AttributeOverride(name = "num", column = @Column(name = "midnum")),
    })
    @Embedded
    private info midInfo;

    protected school () {}

    public school(info elementInfo, info midInfo) {
        this.elementInfo = elementInfo;
        this.midInfo = midInfo;
    }
}

이렇게 info에 mapping 되는 변수가 2개 있으면 하나를 @AttributeOverride로 재정의 해준다.

그러면 충돌이 일어나지 않고 정상적으로 생성, 조회가 되는 것을 볼 수 있다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 8장 (List collection mapping)  (0) 2023.03.18
JPA 7장 (Set collection mapping)  (0) 2023.03.18
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
728x90
  • 직접 할당

@Id 설정 대상에 직접 값 설정

저장하기 전에 생성자를 할당한다, 보통 생성 시점에 전달

@Entity
@Table(name = "user1")
public class User1{
    @Id
    private String name;
    private String email;
    
    protected User() {}
    
    public User (String name, String email){
    	this.name = name;
        this.email = email;
	}
}

이렇게 가장 단순한 방법으로 생성하는 방식이다.

  • 식별 칼럼 방식

칼럼 중에 auto_increment 같이 객체 생성시에 식별값을 설정하지 않는 경우가 있다.

그럴 때에는 설정을 추가하여 persist()를 실행할 때 객체에 식별자 값을 할당하게 할 수 있다.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User2{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    
    protected User2(){}

    public User2(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

그 외에도 몇가지 방식이 더 있지만 많이 사용하지 않기 때문에 이렇게만 알아보도록 하겠다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 7장 (Set collection mapping)  (0) 2023.03.18
JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
728x90
  • Entity 기본 annotation

@Entity: 엔티티 클래스에 설정, 필수!

@Table: mapping 테이블을 지정

@Id: 식별자 속성에 설정, 필수!

@Column: 매핑할 컬럼명 지정, 지정하지 않으면 필드명과 프로퍼티명을 사용

@Enumerated: enum 타입 매핑할 때 설정

 

  • Table annotation

annotation을 생략하면 클래스의 이름과 동일한 이름에 mapping

속성으로는

- name: 테이블 이름

- catalog: 카탈로그 이름

 

  • Enumerated

EnumType.STRING: enum 타입 값 이름을 저장

EnumType.ORDINAL: enum 타입의 값의 순서를 저장

 

 

Mapping이 된 설정의 예를 보도록 하자

//mapping을 합니다.
@Entity
//user 테이블에 mapping 합니다.
@Table(name = "user")
public class User {

	//식별자에 mapping 합니다.
    @Id
    //변수명에 mapping 합니다.
    private String email;
    private String name;
    
    //create_date에 mapping 합니다.
    @Column(name = "create_date")
    private LocalDateTime date;
	
    //열거타입 이름을 값으로 저장
    //grade에 mapping
    @Enumerated(EnumType.STRING)
    private Grade grade;

    protected User(){}
}

 

  • Entity 클래스의 제약 조건

1. 반드시 @Entity를 작성해야함

2. 반드시 @Id를 작성해야함

3. 인자 없는 기본 생성자 필요(해당 생성자는 public이나 protected로 작성이 되어야 함)

4. 최상위 클래스여야 함.

'백엔드 > JPA' 카테고리의 다른 글

JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
JPA 1장 (우선 시작해보기)  (0) 2023.03.15
728x90

저번에 배웠던 CRUD를 사용해서 간단한 프로그램을 구현해보자.

 

package UserProgram;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

public class EMF {

    private static EntityManagerFactory entityManagerFactory;

    public static void init(){
        entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
    }

    public static EntityManager getEntityManager() {
        return entityManagerFactory.createEntityManager();
    }

    public static void close(){
        entityManagerFactory.close();
    }
}

어차피 EntityManagerFactory는 계속 사용을 해야하니, 초기화하고 EntityManagerFactory에서 EntityManager를 가져올 수 있는 class를 작성해준다.

 

당연히 간편하게 사용하기 위해 만드는 것으로 굳이 안 만들어도 되기는 한다.

 

그 다음에 DB에서 사용할 User 객체를 만들어준다.

저번에 사용했던 그대로 사용할 예정이다.

package UserProgram;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import java.time.LocalDateTime;

@Entity
@Table(name = "user")
public class User {

    @Id
    private String email;
    private String name;
    private LocalDateTime create_date;

    protected User(){}

    public User(String email, String name, LocalDateTime create_date) {
        this.email = email;
        this.name = name;
        this.create_date = create_date;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDateTime getCreate_data() {
        return create_date;
    }

    public void setCreate_data(LocalDateTime create_data) {
        this.create_date = create_data;
    }

    @Override
    public String toString() {
        return "User{" +
                "email='" + email + '\'' +
                ", name='" + name + '\'' +
                ", create_data=" + create_date +
                '}';
    }
}

이제 본격적으로 JPA를 사용할 차례이다.

Service 하는 메서드들을 한 곳에 모아 클래스로 작성했다.

 

package UserProgram;

import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityTransaction;

public class UserService {

    public void CreateUser(User user){
        EntityManager entityManager = EMF.getEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            entityManager.persist(user);
            entityTransaction.commit();
        }catch(Exception exception){
            entityTransaction.rollback();
            throw exception;
        } finally {
            entityManager.close();
        }
    }

    public User ReadUser(String email){
        EntityManager entityManager = EMF.getEntityManager();
        try{
            User user = entityManager.find(User.class, email);
            if(user == null) throw new NullPointerException();
            else return user;
        }finally{
            entityManager.close();
        }
    }

    public void UpdateUser(String email, String name){
        EntityManager entityManager = EMF.getEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            User user = entityManager.find(User.class, email);
            if(user == null) throw new NullPointerException();
            user.setName(name);
            entityTransaction.commit();
        }catch (Exception exception){
            entityTransaction.rollback();
            throw exception;
        }finally {
            entityManager.close();
        }
    }

    public void DeleteUser(String email){
        EntityManager entityManager = EMF.getEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try{
            entityTransaction.begin();
            User user = entityManager.find(User.class, email);
            if (user == null) throw new NullPointerException();
            entityManager.remove(user);
            entityTransaction.commit();
        }catch(Exception exception){
            entityTransaction.rollback();
            throw exception;
        }finally {
            entityManager.close();
        }
    }
}

우선 저번에 배운 CRUD를 사용하였고, 다른 점이 있다면 static으로 작성된 EntityManagerFactory를 사용하였다.

우선 예외는 대충 넘기고 나중에 추가로 작성하도록 하겠다.

package UserProgram;

import java.time.LocalDateTime;
import java.util.Scanner;

public class UserProgram {

    private static UserService userService = new UserService();

    private static String email;
    private static String name;

    public static void main(String[] args) {
        EMF.init();

        Scanner scanner = new Scanner(System.in);

        boolean flag = true;

        while(flag){
            System.out.print("1. Create, 2. Read, 3. Update, 4. Delete, 5. exit : ");
            int answer = scanner.nextInt();
            switch(answer){
                case 1:
                    CreateMethod();
                    break;
                case 2:
                    ReadMethod();
                    break;
                case 3:
                    UpdateMethod();
                    break;
                case 4:
                    DeleteMethod();
                    break;
                case 5:
                    flag = false;
                    break;
            }
        }

        EMF.close();
        scanner.close();
    }

    private static void inputEmail(){
        Scanner scanner = new Scanner(System.in);
        System.out.print("input email:");
        email = scanner.nextLine();
    }

    private static void inputName(){
        Scanner scanner = new Scanner(System.in);
        System.out.print("input name:");
        name = scanner.nextLine();
    }

    private static void CreateMethod(){
        inputEmail();
        inputName();
        User user = new User(email, name, LocalDateTime.now());
        try{
            userService.CreateUser(user);
        }catch(Exception exception){
            System.out.println("createError");
        }
    }

    private static void ReadMethod(){
        inputEmail();
        try{
            User user = userService.ReadUser(email);
            System.out.println(user);
        }catch(Exception exception){
            System.out.println("readError");
        }
    }

    private static void UpdateMethod(){
        inputEmail();
        inputName();
        try{
            userService.UpdateUser(email, name);
            System.out.println(userService.ReadUser(email));
        }catch(Exception exception){
            System.out.println("updateError");
        }
    }

    private static void DeleteMethod(){
        inputEmail();
        try{
            userService.DeleteUser(email);
        }catch(Exception exception){
            System.out.println("deleteError");
        }
    }
}

이렇게 프로그램을 작성하였다.

그러면 이렇게 프로그램이 잘 작동한다.

 

값을 추가하고 삭제하는 메서드들도 잘 작동이 된다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
JPA 1장 (우선 시작해보기)  (0) 2023.03.15

+ Recent posts