
JPA는 자바에서 기본으로 제공하는 Collection, List, Set, Map 컬렉션을 지원하고 다음 경우에 이 컬렉션을 사용할 수 있다.
@OneToMany,@ManyToMany를 사용해서 일대다나 다대다 엔티티 관계를 매핑할 때@ElementCollection을 사용해서 값 타입을 하나 이상 보관할 때Map의 경우,@MapKey를 사용해서 매핑할 수 있다.
@Entity
@Getter
public class Team {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	@OneToMany
	@JoinColumn(name = "TEAM_ID")
	private Collection<Member> members = new ArrayList<>();
}
public static void main(String[] args) {
	Team team = new Team();
	System.out.println("before persist = " + team.getMembers().getClass())
	em.persist(team);
	System.out.println("after persist = " + team.getMembers().getClass())
}
// before persist = class java.util.ArrayList
// after persist = class org.hibernate.collection.internal.PersistentBag
| 컬렉션 인터페이스 | 하이버네이트 내장 컬렉션 | 중복 허용 | 순서 보관 | 
|---|---|---|---|
| Collection, List | PersistenceBag | ✅ | ❌ | 
| Set | PersistentSet | ❌ | ❌ | 
| List + @OrderColumn | PersistentList | ✅ | ✅ | 
equals() 메서드를 사용public class Parent {
	@Id
	@GeneratedValue
	private Long id;
	@OneToMany
	@JoinColumn
	private Collection<Child> childs = new ArrayList<>();
}
equals(), hascode() 메서드를 함께 사용해서 비교한다.public class Parent {
	@Id
	@GeneratedValue
	private Long id;
	@OneToMany
	@JoinColumn
	private Set<Child> childs = new HashSet<>();
}
@OrderColumn 사용 시, 순서가 있는 컬렉션으로 인식한다.public class Parent {
	@Id
	@GeneratedValue
	private Long id;
	@OneToMany
	@OrderColumn(name = "INDEX")
	private Collection<Child> childs = new ArrayList<>();
}
public class Child {
	@Id
	@GeneratedValue
	private Long id;
	@ManyToOne
	@JoinColumn(name = "PARENT_ID")
	private Parent parent;
}
@OrderColumn 은 Parent 엔티티에서 매핑하므로, Child는 index 값을 알 수 없다.null 이 보관된다.ORDER BY 절을 사용해서 컬렉션 순서를 정렬한다.@OrderBy 를 적용하면, 순서를 유지하기 위해 HashSet 대신에 LinkedHashSet을 내부에서 사용한다.public class Team {
	@Id
	@GeneratedValue
	private Long id;
	@OneToMany
	@OrderBy("username desc, age asc")
	private Set<Member> members = new HashSet<>();
}
public class Member {
	@Id
	@GeneratedValue
	private Long id;
	private String username;
  private Integer age;
	@ManyToOne
	@JoinColumn(name = "TEAM_ID")
	private Team team;
}
@Converter 를 사용하면, 엔티티의 데이터를 변환해서 데이터베이스에 저장할 수 있다.boolean 값을 'Y' 혹은 'N' 문자로 저장하고 싶을 때!!!!@Entity
public class Member {
	@Id
	private Long id;
	private String username;
	@Convert(converter = BooleanToYNConverter.class)
	private boolean vip;
}
@Converter
public class BooleanToYNConverter implements AttributeConverter<Boolean, String> {
	@Override
	public String convertToDatabaseColumn(Boolean attribute) {
	  return (attribute != null && attribute) ? "Y" : "N";
  }
	public Boolean convertToEntityAttribute(String dbData) {
		return "Y".equals(dbData);
  }
}
AttributeConveter<X, Y> 인터페이스public interface AttributeConverter<X,Y> {
    public Y convertToDatabaseColumn (X attribute);
    public X convertToEntityAttribute (Y dbData);
}
@Convert 클래스 레벨에 설정하기attributeName 속성을 설정해줘야 한다.@Entity
@Convert(converter = BooleanToYNConverter.class, attributeName = "vip")
public class Member {
	@Id
	private Long id;
	private String username;
	private boolean vip;
}
@Converter(autuApply = true) 옵션을 적용하면,@Convert 설정하지 않아도 자동으로 컨버터가 적용된다.@Converter(autuApply = true)
public class BooleanToYNConverter implements AttributeConverter<Boolean, String> {
	@Override
	public String convertToDatabaseColumn(Boolean attribute) {
	  return (attribute != null && attribute) ? "Y" : "N";
  }
	public Boolean convertToEntityAttribute(String dbData) {
		return "Y".equals(dbData);
  }
}
@Convert 속성 정리| 속성 | 기능 | 기본값 | 
|---|---|---|
| converter | 사용할 컨버터 클래스를 지정한다. | |
| attributeName | 컨버터를 적용할 클래스 멤버 필드를 지정한다. | |
| disableConversion | 글로벌 컨버터나 상속받은 컨버터를 사용하지 않는다. | false | 
![]()
| 번호 | Event | Event 시점 | 
|---|---|---|
| 1 | PostLoad | - 엔티티가 영속성 컨텍스트에 조회된 직후  - refresh를 호출한 후 | 
| 2 | PrePersist | persist 호출 직전 (영속성 컨텍스트에 관리되기 직전) | 
| 3 | PreUpdate | flush 나 commit 호출해서, 엔티티를 데이터베이스에 반영하기 직전 | 
| 4 | PreRemove | - remove 를 호출해서 엔티티를 영속성 컨텍스트에서 삭제하기 직전- 삭제 명령어로 영속성 전이가 일어날 때 - orphanRemoval 에 대해서 flush 나 commit 시 | 
| 5 | PostPersist | flush 나 commit 을 호출해서 엔티티를 데이터베이스에 저장한 직후 | 
| 6 | PostUpdate | flush 나 commit 을 호출해서 엔티티를 데이터베이스에 수정한 직후 | 
| 7 | PostRemove | flush 나 commit 을 호출해서 엔티티를 데이터베이스에 삭제한 직후 | 
@Entity
@Slf4j
public class Duck {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  @PrePersist
  public void prePersist() {
		log.info("Duck.prePersist id = " + id);
  }
  @PostPersist
  public void postPersist() {
		log.info("Duck.postPersist id = " + id);
  }
  @PostLoad
  public void postLoad() {
		log.info("Duck.postLoad id = " + id);
  }
  @PreUpdate
  public void preUpdate() {
		log.info("Duck.preUpdate id = " + id);
  }
  @PostUpdate
  public void postUpdate() {
		log.info("Duck.postUpdate id = " + id);
  }
  @PreRemove
  public void preRemove() {
		log.info("Duck.preRemove id = " + id);
  }
  @PostRemove
  public void postRemove() {
		log.info("Duck.postRemove id = " + id);
  }
}
@EntityListeners )@Entity
@EntityListeners(DuckListener.class)
public class Duck {
   ...
}
public class DuckListener {
  @PrePersist
  public void prePersist(Object object) {
		log.info("Duck.prePersist id = " + id);
  }
  @PostPersist
  public void postPersist(Duck duck) {
		log.info("Duck.postPersist id = " + id);
  }
  
}
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
                 version="2.1">
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.sterl.example.EntityListener">
                    <!-- optional, annotations will work too -->
                    <pre-persist method-name="fooMethod"/> 
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>
@ExcludeDefaultListners@ExcludeSuperclassListeners