값 타입

born_a·2022년 9월 20일
0

기본값 타입

값 타입 분류

임베디드 타입, 컬렉션 값 타입은 jpa에서 정의해서 써야 함.

자바의 기본 타입은 절대 공유 X

Integer : 값을 복사하는게 아니라 참조가 넘어가므로 공유가능한 객체이다
ex)

Integer a = new Integer(10);
Integer b = a;

임베디드 타입

Member.java

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;
    
    //기간 Period
    private LocalDateTime startDate;
    private LocalDateTime endDate;

    //주소
    private String city;
    private String street;
    private String zipcode;
}

기간과 주소를 따로 빼보자
Member.java

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    //기간 Period
    @Embedded
    private Period workPeriod;

    //주소
    @Embedded
    private Address homeAddress;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Period getWorkPeriod() {
        return workPeriod;
    }

    public void setWorkPeriod(Period workPeriod) {
        this.workPeriod = workPeriod;
    }

    public Address getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(Address homeAddress) {
        this.homeAddress = homeAddress;
    }
}

Address.java

@Embeddable
public class Address {
    private String city;
    private String street;
    private String zipcode;

    public Address() {
    }

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }
}

Period.java

@Embeddable
public class Period {
    private LocalDateTime startDate;
    private LocalDateTime endDate;


    public LocalDateTime getStartDate() {
        return startDate;
    }

    public void setStartDate(LocalDateTime startDate) {
        this.startDate = startDate;
    }

    public LocalDateTime getEndDate() {
        return endDate;
    }

    public void setEndDate(LocalDateTime endDate) {
        this.endDate = endDate;
    }
}

한 엔티티에서 같은 값 타입을 사용하면?

@AttributeOverride: 속성 재정의

하나면 @AttributeOverride, 여러개 속성이면 @AttributeOverrides 사용
Member.java

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    //기간 Period
    @Embedded
    private Period workPeriod;

    //주소
    @Embedded
    private Address homeAddress;
    
    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "city",
            column = @Column(name="WORK_CITY")),
            @AttributeOverride(name = "street",
                    column = @Column(name="WORK_STREET")),
            @AttributeOverride(name = "zipcode",
                    column = @Column(name="WORK_ZIPCODE"))
    })
    private Address workAddress;

값 타입과 불변 객체

JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Address address = new Address("city", "streer", "10000");

            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(address);
            em.persist(member);

            Member member2 = new Member();
            member2.setUsername("member2");
            member2.setHomeAddress(address);
            em.persist(member2);

            member.getHomeAddress().setCity("newCity");


            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}

두 멤버의 city가 모두 newCity로 바뀌었다.

값을 복사해서 넣어보자

JpaMain.java

try {
            Address address = new Address("city", "streer", "10000");

            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(address);
            em.persist(member);

            Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());

            Member member2 = new Member();
            member2.setUsername("member2");
            member2.setHomeAddress(copyAddress);
            em.persist(member2);

            member.getHomeAddress().setCity("newCity"); //1번째에만 영향을 줌


            tx.commit();
        }


1번째 member에만 영향을 미치는 것을 확인 가능 .

객체 타입의 한계

: a와 b 둘 다 같은 address 인스턴스를 바라보고 있음.

객체 타입을 수정할 수 없게 만들면 된다.

불변객체

생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨
or setter 를 private으로 만들면 됨.

그럼 값을 변경할려면 어떻게 하지?
완전히 다시 세팅한다. 값을 통으로 다 바꿔야한다.

JpaMain.java

try {
            Address address = new Address("city", "streer", "10000");

            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(address);
            em.persist(member);

            Address newAddress = new Address("WowCity", address.getStreet(), address.getZipcode());

            member.setHomeAddress(newAddress);

            tx.commit();
        }

값 타입의 비교

객체 타입은 == 비교하면 false가 나옴

ValueMain.java

public class ValueMain {
    public static void main(String[] args) {
        int a = 10;
        int b = 10;

        System.out.println("a == b : " + (a == b));

        Address address1 = new Address("city", "streer", "10000");
        Address address2 = new Address("city", "streer", "10000");

        System.out.println("address1 == address2 : "+(address1 == address2));
    }
}

constructor 로 equals() 메소드를 생성한다.
Address.java

 @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(city, address.city) && Objects.equals(street, address.street) && Objects.equals(zipcode, address.zipcode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(city, street, zipcode);
    }

equals() 구현하면 hashCode()도 그에 맞춰서 구현해야한다.

ValueMain.java

public class ValueMain {
    public static void main(String[] args) {
        int a = 10;
        int b = 10;

        System.out.println("a == b : " + (a == b));

        Address address1 = new Address("city", "streer", "10000");
        Address address2 = new Address("city", "streer", "10000");

        System.out.println("address1 equals  address2 : "+(address1.equals(address2)));
    }
}

객체의 값을 비교할 때는 equals를 쓰자

값 타입 컬렉션

값 타입은 값 속성 들을 모아서 pk로 지정한다.

Member.java

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    //주소
    @Embedded
    private Address homeAddress;

    @ElementCollection
    @CollectionTable(name = "FAVORITE_FOOD", joinColumns =
        @JoinColumn(name = "MEMBER_ID")) //member_id를 외래키로 잡게됨
    @Column(name = "FOOD_NAME") //이렇게 속성 값이 하나면 컬럼명 지정 가능
    private Set<String> favoriteFoods = new HashSet<>();

    @ElementCollection
    @CollectionTable(name = "ADDRESS", joinColumns =
        @JoinColumn(name = "MEMBER_ID"))
    private List<Address> addressHistory = new ArrayList<>();

값 타입 컬렉션도 지연 로딩 전략 사용
JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(new Address("homeCity","street","1000"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("족발");
            member.getFavoriteFoods().add("피자");

            member.getAddressHistory().add(new Address("old1","street","1000"));
            member.getAddressHistory().add(new Address("old2","street","1000"));

            em.persist(member);

            em.flush();
            em.clear(); //db에 넣고, Db에는 데이터가 있는 상태에서 깔끔한 상태로 조회 가능

            System.out.println("=======================");
            Member findMember = em.find(Member.class, member.getId());

            List<Address> addressHistory = findMember.getAddressHistory();
            for (Address address : addressHistory) {
                System.out.println("address = " + address.getCity());
            }

            Set<String> favoriteFoods = findMember.getFavoriteFoods();
            for (String favoriteFood : favoriteFoods) {
                System.out.println("favoriteFood = " + favoriteFood);
            }
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}

=====표시선 아래로 addressHistory와 favoriteFood가 조회됨을 확인할 수 있다.

값 타입 수정

Address 인스턴스 자체를 갈아끼워야한다.

JpaMain.java

 try {
            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(new Address("homeCity","street","1000"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("족발");
            member.getFavoriteFoods().add("피자");

            member.getAddressHistory().add(new Address("old1","street","1000"));
            member.getAddressHistory().add(new Address("old2","street","1000"));

            em.persist(member);

            em.flush();
            em.clear(); //db에 넣고, Db에는 데이터가 있는 상태에서 깔끔한 상태로 조회 가능

            System.out.println("=======================");
            Member findMember = em.find(Member.class, member.getId());

            Address a = findMember.getHomeAddress();
            findMember.setHomeAddress(new Address("newCity",a.getStreet(), a.getZipcode()));
            
            tx.commit();
        }


select 쿼리 이후에 update 쿼리가 나감.

String은 값 타입이다. 통째로 갈아 끼워야한다. update 자체가 없음

//치킨 -> 한식
            findMember.getFavoriteFoods().remove("치킨");
            findMember.getFavoriteFoods().add("한식");

이제 주소를 변경해보자

            findMember.getAddressHistory().remove(new Address("old1", "street", "10000")); //이렇게 해줘야 Object를 정확하게 찾아서 지워줄 수 있음
            findMember.getAddressHistory().add(new Address("newCity1", "street", "10000"));


delete 쿼리 1개, insert쿼리는 2개 나갔다. 왜일까?
->값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장하기 때문!

값 타입 컬렉션 대안

JpaMain.java

try {
            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(new Address("homeCity","street","1000"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("족발");
            member.getFavoriteFoods().add("피자");

            member.getAddressHistory().add(new AddressEntity("old1","street","10000"));
            member.getAddressHistory().add(new AddressEntity("old2","street","10000"));

            em.persist(member);

            em.flush();
            em.clear(); //db에 넣고, Db에는 데이터가 있는 상태에서 깔끔한 상태로 조회 가능

            System.out.println("=======================");
            Member findMember = em.find(Member.class, member.getId());

//            Address a = findMember.getHomeAddress();
//            findMember.setHomeAddress(new Address("newCity",a.getStreet(), a.getZipcode()));

            //치킨 -> 한식
//            findMember.getFavoriteFoods().remove("치킨");
//            findMember.getFavoriteFoods().add("한식");

            //주소 변경
//            findMember.getAddressHistory().remove(new AddressEntity("old1", "street", "10000")); //이렇게 해줘야 Object를 정확하게 찾아서 지워줄 수 있음
 //           findMember.getAddressHistory().add(new AddressEntity("newCity1", "street", "10000"));

            tx.commit();
        }

자체적인 ID가 있다는 것은 값 타입이 아니라 엔티티라는 것.

0개의 댓글

관련 채용 정보