@Entity : JPA가 관리함을 의미 public, protected 생성자, 기본 생성자 필수 @Table(name = "데이터베이스 테이블명") 실행 시점에 테이블 자동 생성 가능 create, create-drop, update, validate, auto, none 개발 초기 단계는 create, update 테스트 서버는 update, validate 스테이징, 운영 서버 validate, none @Column의 옵션은 JPA 로직과는 상관이 없고, 생성시 ddl에 영향을 줌 updatable = false, 업데이트시에 업데이트 하지 않음 nullable = false unique = true, 생성시 이름이 sql은 date=날짜, time=시간, tiemstamp만 있음 GeneratorType.IDENTIFY persist()하면 쿼리가 실행됨 GeneratorType.SEQUENCE 트랜잭션을 해야해야 쿼리가 실행됨 실전 예제
'객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.' - 조영호(객체지향의 사실과 오해), 오브젝트 JPA 포인터 역할 양방향 연관관계의 주인 객체와 테이블의 차이를 이해해야 됨 테이블은 외래키만으로 양방향이 모두 적용됨(조인...) 객체는 그렇지 않음 양방향, 단방향... 양방향이 좋은가? 기본적으로 단방향으로 하는게 좋다. 설계시에 단방향 매핑만으로 이미 연관관계 매핑 완료하자 개발시에 꼭 필요하면 추가 하자.
// 양쪽값 public void addMember(MemberHello member) { member.setTeam(this); members.add(member); } // 양쪽 값 public void changeTeam(Team team) { this.team = team; team.getMembers().add(this); // 연관관계 편의 메소드 }
관계형 데이터베이스는 상속관계가 없다 객체는 상속관계는 데이터 베이스에서는 조인, 단일 테이블, 각각테이블로 구현이 가능함 기본 전략은 단일 테이블 @MappedSuperclass - 자주 애용하자 테이블과 관계가 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할 abstract(추상 클래스)로 사용 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용 @Entity 클래서는 엔티티나 @MappedSuperclass로 지정한 클래스만 상속 가능
멤버를 조회할때 팀도 함께 조회를 해야할까?
em.getReference() //
프록시 클래스
실제 클래스를 상속 받아서 만들어짐
실제 클래스와 겉 모양이 같다.
특징
프록시 객체는 처음 사용할 때 한 번만 초기화(DB에서 가져와
프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아니고, 초기화 이후 프록시 객체를 통해서 실제 엔티티에 접근 가능
프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 == 대신 instanceof 사용해야 됨
영속성 컨텍스트에 엔티티가 있으면 em.getReference()를 호출해도 실제 엔티티 반환(프록시 아님)
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생
JPA내에서는 프록시든, 객체든 항상 == true 임을 보장함
프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법
entity.getClass().getName() 출력
프록시 강제 초기화, JPA는 강제 초기화가 없음
org.hibernate.Hibernate.initialize(entity);
즉시 로딩(EAGER), 지연 로딩(LAZY)
실무에서 가급적 모든 연관관계는 지연 로딩만 사용
즉시 로딩은 JPQL에서 N+1 문제 발생
@ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정
@OneToMany, @ManyToMany는 기본이 지연 로딩
@필요하면 Fetch join, annotation, 배치 사이즈로 사용함
모든 연관관계에 지연 로딩을 사용하라
실무에서 즉시 로딩을 사용하지 마라!, JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해라!
즉시 로딩은 상상하지 못한 쿼리가 나간다.
영속성 전이(CASCADE)
영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음
다만 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐
CASCADE.ALL or CASCADE.PERSIST를 주로 사용
게시판 - 첨부 파일의 경우 처럼 거의 라이프사이클이 같을 때 유용하게 사용
소유자가 하나일때 사용
고아 객체(삭제는 언제나 조심해서 사용)
orphanRemoval = true
연결고리가 없어졌을때 삭제 됨
참조하는 곳이 하나일 때 사용해야 됨
특정 엔티티가 개인 소유할 때 사용 (@OneToOne, @OneToMany만 가능)
CASCADE.ALL, orphanRemoval = true를 모두 사용하면
부모 엔티티를 통해서 자식의 생명 주기를 관리할 수 있음(DDD의 Aggregate Root개념을 구현할 때 유용)
실전:
자바 기본값 타입은 공유가 불가능(복사) 래퍼 클래스는 공유가 가능하지만 변경할 방법이 없음 임베디드 타입(클래스 생성 -> 복합값을 가짐) 재사용 가능 높은 응집도를 가짐 엔티티가 생명 주기를 조절 객체와 테이블을 아주 세밀하게 매핑하는 것이 가능( find-grained) 잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다. 동일한 타입을 중복으로 사용하면 중복필드로 오류가 남 @AttributeOverrides, @AttributeOverride 사용 임베디드 타입은 복사해서 사용해야 됨 (= <-레퍼런스(참조) 복사, new 사용해야 됨) 공유로 인한 사이드 이펙트는 주의해야 한다. 대신 불변 객체(immutable object)로 설계하거나 생성자로만 값을 설정하고, 수정자로는 변경이 안되게 함(불변객체, 없애거나 private로 함) 변경하려면 new를 기존 값을 가져오고, 변경하고 싶은 것만 변경하면 됨(임베디드 타입 객체 전체를 바꿔야 함. 일부만 바꾸지 말자!!) 실전: 임베디드 타입은 사용할 경우 불변 객체로 해서 사용하면 주석을 꼭 만들자!! 기본 값타입이 인스턴스가 달라도 값이 같으면 같다. 값 비교시 동일성(identity) 비교: 인스턴스의 참조 값 비교, == 동등성(equivalence) 비교: 인스턴스의 값 비교, equals() 객체 비교시 equals() override하여 전체 값을 일일이 동등성 비교를 해야 됨. (툴에서 자동으로 제공하는 것으로 사용, hashCode도 함께...) 값타입 컬렉션(@ElementCollection, @CollectionTable 사용) 엔티티로 컬렉션이 아닌 값타입으로 컬렉션을 사용 데이터베이스로 봐서는 테이블이 분리된 것임 테이블이 분리되었어도 생명주기가 엔티티에 의해 관리가 됨 즉 영속성 전이, 고아 객체 제거 기능을 필수로 가지는 것과 같음 값 타입 컬렉션도 지연 로딩 전략을 사용함 수정시에는 세터가 아닌 new를 사용하여 통으로 교체(side effect를 원천적으로 없앰) 값 타입은 엔티티와 다르게 식별가 개념이 없다. 값은 변경하면 추적이 어렵다. 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다. 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야 함(null X, 중복 x) 실무에서는 값타입을 엔티티로 승격하여 사용(일대다 관계) 값 타입 컬렉션은 단순한 값 저장시에만 사용
Tip
아래 코드에서 3곳만 고치면 된다.
<property name=“dataSource” ref=“” />
<property name=“packagesToScan” value=“” >
<prop key=“hibernate.dialect”>
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 컨테이너가 관리하는 EntityManager 생성, @PersistenceContext와 함께 사용 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="egov.dataSource" /> <!-- 어노테이션 매핑정보 스캔 --> <property name="packagesToScan" value="com" /> <!-- 구현체별 자체 기능을 표준화 --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> </bean> </property> <!-- persistence.xml 설정정보와 함께 사용가능 --> <property name="jpaProperties"> <props> <!-- <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.EJB3NamingStrategy</prop> --> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <!-- hibernate 5 버전에서의 세팅 (4 버전은 위에껄 사용)- <prop key="hibernate.naming.implicit-strategy">org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl</prop> <prop key="hibernate.naming.physical-strategy">org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl</prop> --> <prop key="hibernate.hbm2ddl.auto">validate</prop> <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.use_sql_comments">true</prop> <prop key="hibernate.jdbc.batch_size">5</prop> </props> </property> </bean> </beans>
DB2 | org.hibernate.dialect.DB2Dialect |
DB2 AS/400 | org.hibernate.dialect.DB2400Dialect |
DB2 OS390 | org.hibernate.dialect.DB2390Dialect |
PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
MySQL5 | org.hibernate.dialect.MySQL5Dialect |
MySQL5 with InnoDB | org.hibernate.dialect.MySQL5InnoDBDialect |
MySQL with MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
Oracle (any version) | org.hibernate.dialect.OracleDialect |
Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
Oracle 11g | org.hibernate.dialect.Oracle10gDialect |
Sybase | org.hibernate.dialect.SybaseASE15Dialect |
Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialect |
Microsoft SQL Server 2000 | org.hibernate.dialect.SQLServerDialect |
Microsoft SQL Server 2005 | org.hibernate.dialect.SQLServer2005Dialect |
Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
SAP DB | org.hibernate.dialect.SAPDBDialect |
Informix | org.hibernate.dialect.InformixDialect |
HypersonicSQL | org.hibernate.dialect.HSQLDialect |
H2 Database | org.hibernate.dialect.H2Dialect |
Ingres | org.hibernate.dialect.IngresDialect |
Progress | org.hibernate.dialect.ProgressDialect |
Mckoi SQL | org.hibernate.dialect.MckoiDialect |
Interbase | org.hibernate.dialect.InterbaseDialect |
Pointbase | org.hibernate.dialect.PointbaseDialect |
FrontBase | org.hibernate.dialect.FrontbaseDialect |
Firebird | org.hibernate.dialect.FirebirdDialect |
Tip
아래 코드에서 한개만 고치면 된다.
<jpa:repositories base-package=“”>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jpa="http://www.springframework.org/schema/data/jpa" 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-4.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <jpa:repositories base-package="com"></jpa:repositories> </beans>
<properties> <egovframework.jpa.version>3.7.0</egovframework.jpa.version> <hibernate.version>4.3.11.Final</hibernate.version> </properties> <dependencies> <dependency> <groupId>egovframework.rte</groupId> <artifactId>egovframework.rte.psl.data.jpa</artifactId> <version>${egovframework.jpa.version}</version> </dependency> <!-- Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.2.Final</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.11.10.RELEASE</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </exclusion> </exclusions> </dependency> </dependencies>