HIBER NATE 5.x 시작하기 |
수익성의 성격을 띄지 않는다면
재배포, 수정 가능합니다.
https://github.com/visualkhh/book-hibernate
(2016. 12. 21)
ORM (Object Relational Mapping) 무엇인가?
JPA (Java Persistence API) 무엇인가?
1:N, N:1, N:N 컬렉션 영속화 (List, Set, Map, Array[])
Groovy Template 이용하여 Dynamic Query 사용하기
ORM (Object Relational Mapping) 무엇인가?
RDB 테이블을 객체지향적으로 사용하기 위한 기술입니다. RDB 은 객체지향적 (상속, 다형성, 레퍼런스, 오브젝트 등)으로 접근하기 쉽지 않습니다.
때문에 ORM을 사용해 오브젝트와 RDB 사이에 객체지향적으로 다루기 위한 기술입니다.
JPA (Java Persistence API) 무엇인가?
ORM 전문가가 참여한 EJB 3.0 스펙 작업에서 기존 EJB ORM이던 Entity Bean을 JPA라고 바꾸고 JavaSE, JavaEE를 위한 영속성(persistence) 관리와 ORM을 위한 표준 기술입니다. JPA는 ORM 표준 기술로 Hibernate, OpenJPA, EclipseLink, TopLink Essentials과 같은 구현체가 있고 이에 표준 인터페이스가 바로 JPA입니다.
Boss에서 개발한 ORM(Object Relational Mapping) 프레임워크 입니다.
장점
Hibernate는 특정 클래스에 매핑되어야 하는 데이터베이스의 테이블에 대한 관계 정의가 되어 있는 XML 파일의 메타데이터로 객체관계 매핑을 간단하게 수행시킵니다.
Hibernate를 사용하면 데이터베이스가 변경되더라도 SQL 스크립트를 수정하는등의 작업을 할 필요가 없습니다.
애플리케이션에서 사용되는 데이터베이스를 변경시키고자 한다면 설정파일의 dialect 프로퍼티를 수정함으로서 쉽게 처리할 수 있습니다.
Hibernate는 MySQL, Oracle, Sybase, Derby, PostgreSQL를 포함한 많은 데이터베이스를 지원하며 POJO기반의 모델과도 원활하게 동작합니다.
- 쿼리가 변경되면 이에따른 프로그램 소스 DTO객체의 변경도 불가피하게 일어난다
- 데이터를 가져와 객체지향적으로 관계를 Mapping하는 일이 매번 일어난다.
!!SQL의존적인 개발이 이루어진다.
관계형 데이터베이스에는 로우와 컬럼의 2차원 형태로 데이터가 저장된다. 데이터 관계는 외래키foreign key 형태로 표현된다. 문제는 도메인 객체를 관계형 데이터 베이스로 저장할 때 발생한다. 애플리케이션의 객체는 로우와 컬럼 형태가 아니다. 도메인 객체는 객체의 상태를 속성(변수)으로 가지고 있다. 그래서 도메인 객체 그대로 관계형 데이터베이스에 저장할 수가 없다. 이러한 불일치를 객체-관계 간 임피던스 불일치object-relational impedance mismatch라고 합니다.
상속은 객체 세계에서는 지원하지만, 관계형 스키마에서는 지원하지 않는다. 상속은 모든 객체지향 언어, 특히 자바에서 바늘과 실처럼 뗄 수 없는 특징입니다. 안타깝게도 관계형 스키마에는 상속 개념이 없습니다. 회사에서 임원과 직원의 예를 들어보면. 임원 개인도 회사의 직원이죠. 이 관계를 데이터베이스에서 표현하는 것은 테이블 간 관계 수정이 필요해서 쉽지 않습니다. 상속 없이 현실 세계의 문제 상황을 표현하는 것은 매우 복잡한 일입니다. 그런데 데이터베이스는 상속 관계와 같은 형태를 알지 못하지요. 이것을 해결할 간단한 방법은없지만, 문제를 풀 수 있는 몇 가지 접근법이 있습니다. 이 접근법은 다양한 클래스-테이블class-to-table 전략을 사용합니다.
1. SQL 중심적인 개발의 문제점
- field 하나추가시 쿼리도 바꿔야하고 VO도 바꿔야되고 ...
- SQL에 의존적인 개발을 피하기 어렵다.
- 객체답게 모델링 할수록 매핑 작업만 늘어난다
- 객체지향적으로 데이터를 관리할 수 있기 때문에 비즈니스 로직에 집중 할 수 있으며,
객체지향 개발이 가능합니다.
- 테이블 생성, 변경, 관리가 쉽다. (JPA를 잘 이해하고 있는 경우) 로직을 쿼리에 집중하기 보다는
객체자체에 집중 할 수 있습니다.
- 빠른 개발이 가능합니다.
- 어렵다. 장점을 더 극대화 하기 위해서 알아야 할게 많습니다.
- 잘 이해하고 사용하지 않으면 독이될수도 있습니다.
- 성능상 문제가 있을 수 있다.(이 문제 또한 잘 이해해야 해결이 가능합니다.
퍼시스턴트 객체를 처음 만들었을 때의 상태. 데이터베이스 테이블에 관련 데이터가 없으며, 연관된 Session이 없다.
현재 활성화된 Session과 연결된 퍼시스턴트 객체. 이 상태의 퍼시스턴트 객체는 고유성을 가지며, 프로퍼티 값의 변경이 Session을 통해 자동으로 데이터베이스에 반영된다.
영속 상태의 퍼시스턴트 객체가 Session과 연결이 끊기면 준영속 상태가 된다. Hibernate의 관리를 받지는 않지만, 영속 데이터를 갖고 있다.
|
|
|
|
|
group 'com.khh' /*groovy*/
|
build.gradle |
Hibernate Setting (Properties Set)
package com.khh.hibernate.c0; |
매개변수로 표준 가상머신Standard VM 인수 형식을 사용할수 있다 |
-Dhibernate.connection.url=jdbc:derby:memory:JH;create=true -Dhibernate.username=mk |
<?xml version="1.0" encoding="UTF-8"?> |
org.hibernate.SessionFactory 클래스에서 제공하는 SessionFactory는 Session 인스턴스를 생성하는 팩토리 클래스factory class다. 이는 Thread-safe한 객체이므로 데이터가 의도하지 않게 바뀌는 것을 염려하지 않고, 여러 클래스에서사용해도 된다. Session 인스턴스가 생성될 때 매핑 정보도 함께 전달하므로 컴파
일된 형태로 모든 매핑 데이터를 가진다
캐시의 두 번째 수준을 관리한다. 이는 애플리케이션을 구성하는 모든 컴포넌트에도 해당된다. 전역global 캐시는 데이터베이스에서 이미 가져온동일한 결과를 여러 애플리케이션에서 요청하는 경우 사용된다.
SessionFactory가 데이터베이스 연결을 위한 열쇠 꾸러미를 가지고 있다면,
Session은 데이터베이스 접속과 데이터 이동이 이루어지는 열쇠 자체다. Session은 싱글 스레드single-threaded 객체이므로 여러 컴퍼넌트에 같이 선언되어 사용해서는 안 된다. 하나의 작업 단위를 뜻한다. 팩토리에서 세션을 가져오려면 factory.getCurrentSession() 메소드를 사용한다. 세션 객체를 얻으면 한 개의 트랜잭션
안에서 데이터베이스 작업을 수행한다. 세션과 트랜잭션은 밀접한 관련이 있다. 다음은 세션과 트랜잭션의 생애 주기를 보여주고 있다.
Table 1. Standard BasicTypes |
|||
Hibernate type (org.hibernate.type package) |
JDBC type |
Java type |
BasicTypeRegistry key(s) |
StringType |
VARCHAR |
java.lang.String |
string, java.lang.String |
MaterializedClob |
CLOB |
java.lang.String |
materialized_clob |
TextType |
LONGVARCHAR |
java.lang.String |
text |
CharacterType |
CHAR |
char, java.lang.Character |
char, java.lang.Character |
BooleanType |
BIT |
boolean, java.lang.Boolean |
boolean, java.lang.Boolean |
NumericBooleanType |
INTEGER, 0 is false, 1 is true |
boolean, java.lang.Boolean |
numeric_boolean |
YesNoType |
CHAR, 'N'/'n' is false, 'Y'/'y' is true. The uppercase value is written to the database. |
boolean, java.lang.Boolean |
yes_no |
TrueFalseType |
CHAR, 'F'/'f' is false, 'T'/'t' is true. The uppercase value is written to the database. |
boolean, java.lang.Boolean |
true_false |
ByteType |
TINYINT |
byte, java.lang.Byte |
byte, java.lang.Byte |
ShortType |
SMALLINT |
short, java.lang.Short |
short, java.lang.Short |
IntegerTypes |
INTEGER |
int, java.lang.Integer |
int, java.lang.Integer |
LongType |
BIGINT |
long, java.lang.Long |
long, java.lang.Long |
FloatType |
FLOAT |
float, java.lang.Float |
float, java.lang.Float |
DoubleType |
DOUBLE |
double, java.lang.Double |
double, java.lang.Double |
BigIntegerType |
NUMERIC |
java.math.BigInteger |
big_integer, java.math.BigInteger |
BigDecimalType |
NUMERIC |
java.math.BigDecimal |
big_decimal, java.math.bigDecimal |
TimestampType |
TIMESTAMP |
java.sql.Timestamp |
timestamp, java.sql.Timestamp |
TimeType |
TIME |
java.sql.Time |
time, java.sql.Time |
DateType |
DATE |
java.sql.Date |
date, java.sql.Date |
CalendarType |
TIMESTAMP |
java.util.Calendar |
calendar, java.util.Calendar |
CalendarDateType |
DATE |
java.util.Calendar |
calendar_date |
CalendarTimeType |
TIME |
java.util.Calendar |
calendar_time |
CurrencyType |
java.util.Currency |
VARCHAR |
currency, java.util.Currency |
LocaleType |
VARCHAR |
java.util.Locale |
locale, java.utility.locale |
TimeZoneType |
VARCHAR, using the TimeZone ID |
java.util.TimeZone |
timezone, java.util.TimeZone |
UrlType |
VARCHAR |
java.net.URL |
url, java.net.URL |
ClassType |
VARCHAR (class FQN) |
java.lang.Class |
class, java.lang.Class |
BlobType |
BLOB |
java.sql.Blob |
blog, java.sql.Blob |
ClobType |
CLOB |
java.sql.Clob |
clob, java.sql.Clob |
BinaryType |
VARBINARY |
byte[] |
binary, byte[] |
MaterializedBlobType |
BLOB |
byte[] |
materized_blob |
ImageType |
LONGVARBINARY |
byte[] |
image |
WrapperBinaryType |
VARBINARY |
java.lang.Byte[] |
wrapper-binary, Byte[], java.lang.Byte[] |
CharArrayType |
VARCHAR |
char[] |
characters, char[] |
CharacterArrayType |
VARCHAR |
java.lang.Character[] |
wrapper-characters, Character[], java.lang.Character[] |
UUIDBinaryType |
BINARY |
java.util.UUID |
uuid-binary, java.util.UUID |
UUIDCharType |
CHAR, can also read VARCHAR |
java.util.UUID |
uuid-char |
PostgresUUIDType |
PostgreSQL UUID, through Types#OTHER, which complies to the PostgreSQL JDBC driver definition |
java.util.UUID |
pg-uuid |
SerializableType |
VARBINARY |
implementors of java.lang.Serializable |
Unlike the other value types, multiple instances of this type are registered. It is registered once under java.io.Serializable, and registered under the specific java.io.Serializable implementation class names. |
StringNVarcharType |
NVARCHAR |
java.lang.String |
nstring |
NTextType |
LONGNVARCHAR |
java.lang.String |
ntext |
NClobType |
NCLOB |
java.sql.NClob |
nclob, java.sql.NClob |
MaterializedNClobType |
NCLOB |
java.lang.String |
materialized_nclob |
PrimitiveCharacterArrayNClobType |
NCHAR |
char[] |
N/A |
CharacterNCharType |
NCHAR |
java.lang.Character |
ncharacter |
CharacterArrayNClobType |
NCLOB |
java.lang.Character[] |
N/A |
Table 2. Java 8 BasicTypes |
|||
Hibernate type (org.hibernate.type package) |
JDBC type |
Java type |
BasicTypeRegistry key(s) |
DurationType |
BIGINT |
java.time.Duration |
Duration, java.time.Duration |
InstantType |
TIMESTAMP |
java.time.Instant |
Instant, java.time.Instant |
LocalDateTimeType |
TIMESTAMP |
java.time.LocalDateTime |
LocalDateTime, java.time.LocalDateTime |
LocalDateType |
DATE |
java.time.LocalDate |
LocalDate, java.time.LocalDate |
LocalTimeType |
TIME |
java.time.LocalTime |
LocalTime, java.time.LocalTime |
OffsetDateTimeType |
TIMESTAMP |
java.time.OffsetDateTime |
OffsetDateTime, java.time.OffsetDateTime |
OffsetTimeType |
TIME |
java.time.OffsetTime |
OffsetTime, java.time.OffsetTime |
OffsetTimeType |
TIMESTAMP |
java.time.ZonedDateTime |
ZonedDateTime, java.time.ZonedDateTime |
Table 3. Hibernate Spatial BasicTypes |
|||
Hibernate type (org.hibernate.spatial package) |
JDBC type |
Java type |
BasicTypeRegistry key(s) |
JTSGeometryType |
depends on the dialect |
com.vividsolutions.jts.geom.Geometry |
jts_geometry, or the classname of Geometry or any of its subclasses |
GeolatteGeometryType |
depends on the dialect |
org.geolatte.geom.Geometry |
geolatte_geometry, or the classname of Geometry or any of its subclasses |
클래스를 영속화하려면 먼저 엔티티로 정의해야 하는데, @Entity 어노테이션으로 정의할 수 있다
또한 xml으로도 지정가능합니다.
package com.khh.hibernate.c1.entity; |
drop table UserNormal if exists
create table UserNormal ( seq integer not null, address varchar(255), age integer, name varchar(255), primary key (seq) ) |
기본값으로 Entity 클래스 이름이 테이블 이름으로 적용되고 필드 이름이 컬럼 이름으로 생성된다. |
Table name, Column name을 직접 지정할수 있다.
package com.khh.hibernate.c1.entity; |
drop table UserNormal if exists
create table USER ( JUMIN_NUMBER integer not null, USER_ADDR varchar(255), USER_AGE integer, USER_NAME varchar(255), primary key (JUMIN_NUMBER) ) |
각 객체는 유일한 식별자를 가지고 데이터베이스에 영속화되어야 한다. 이때 식별자를 자동으로 생성하는 다양한 방법을 활용할 수 있다.
어노테이션을 추가하면 요구 사항에 맞춘 다른 생성 방법을 설정할 수 있다.
@GenerateValue 어노테이션은 strategy와 generator 두 가지 속성이 있는데,
strategy 속성은 사용할 식별자 생성 타입을 가리키고 generator속성은 식별자를 생성할 메소드를 정의한다.
다음 코드는 ID 생성을 위한 IDENTITY 방법을 보여준다
기본값 : GenerationType.AUTO
GenerationType.AUTO |
기본 방법으로 다른 데이터베이스 간에도 이용할 수 있다. 하이버네이트에서는 데이터베이스를 기반으로 적절한 ID를 선택한다. |
GenerationType.IDENTITY |
이 설정은 몇몇 데이터베이스에서 제공하는 identity 함수를 기반으로 한다. 데 이터베이스에서 고유한 식별자를 제공하는 역할을 한다. |
GenerationType.SEQUENCE |
몇몇 데이터베이스에서는 연속된 숫자에 관한 메커니즘을 제공하는데, 하이버 네이트에서는 일련번호를 사용한다. |
GenerationType.TABLE |
다른 테이블의 고유한 컬럼 값에서 기본키를 생성하는데, 이 경우 TABLE 생성자 를 사용한다. 시퀀스seqeunce 방법에서는 strategy와 generator 속성을 모두 정의해야 한다. |
GenerationType.AUTO
@Data |
create sequence hibernate_sequence start with 1 increment by 1 create table UserGen_AUTO ( seq integer not null, address varchar(255), age integer, name varchar(255), primary key (seq) ) |
자동으로 hibernate_sequence를 생성한다. |
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
|
call next value for hibernate_sequence
insert into UserGen_AUTO (address, age, name, seq) values (?, ?, ?, ?) binding parameter [1] as [VARCHAR] - [addr] binding parameter [2] as [INTEGER] - [100] binding parameter [3] as [VARCHAR] - [name] binding parameter [4] as [INTEGER] - [1]
|
GenerationType.IDENTITY
@Data |
create table UserGen_IDENTITY ( seq integer generated by default as identity, address varchar(255), age integer, name varchar(255), primary key (seq) ) ) |
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
|
insert into UserGen_IDENTITY (seq, address, age, name) values (null, ?, ?, ?) binding parameter [1] as [VARCHAR] - [addr] binding parameter [2] as [INTEGER] - [100] binding parameter [3] as [VARCHAR] - [name] |
GenerationType.SEQUENCE
@Data |
create sequence EMP_SEQ_GEN start with 1 increment by 50
create table UserGen_SEQUENCE ( seq integer not null, address varchar(255), age integer, name varchar(255), primary key (seq) ) |
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
|
call next value for EMP_SEQ_GEN
insert into UserGen_SEQUENCE (address, age, name, seq) values (?, ?, ?, ?) 15:18 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [addr] 15:18 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [INTEGER] - [100] 15:18 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [name] 15:18 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [4] as [INTEGER] - [1]
|
GenerationType.TABLE
@Data |
create table USER_ID_TABLE ( sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name) )
create table UserGen_TABLE ( seq integer not null, address varchar(255), age integer, name varchar(255), primary key (seq) ) |
|
▼실행 |
|
|
Session session = getSessionFactory().getCurrentSession(); |
|
|
select tbl.next_val from USER_ID_TABLE tbl where tbl.sequence_name=? for update
insert into USER_ID_TABLE (sequence_name, next_val) values (?,?)
update USER_ID_TABLE set next_val=? where next_val=? and sequence_name=?
select tbl.next_val from USER_ID_TABLE tbl where tbl.sequence_name=? for update
update USER_ID_TABLE set next_val=? where next_val=? and sequence_name=?
insert into UserGen_TABLE (address, age, name, seq) values (?, ?, ?, ?) 13:35 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [null] 13:35 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [INTEGER] - [null] 13:35 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [name] 13:35 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [4] as [INTEGER] - [1] |
복합 아이디composite-id 식별자 설정과 관련된 세 가지 방법
1. @Embededdable
2. @EmbeddedId
3. @IdClass
ID 값으로 사용되는 클래스에서 구현해야될것들 1. Default Constructor() 2. hashCode() 3. equals() 4. implements Serializable |
tip : Lombok lib사용한다면 위 내용을 자동으로 생성해준다.( https://projectlombok.org/ ) |
@Data |
@Data |
|
create table UserInfo ( name varchar(255) not null, number integer not n ull, address varchar(255), age integer, primary key (name, number) ) |
||
Session session = getSessionFactory().getCurrentSession(); |
insert into UserInfo_Embededdable (address, age, name, number) values (?, ?, ?, ?) binding parameter [1] as [VARCHAR] - [korea] binding parameter [2] as [INTEGER] - [53] binding parameter [3] as [VARCHAR] - [name] binding parameter [4] as [INTEGER] - [128]
|
|
//select
|
select userinfo_e0_.name as name1_1_0_, userinfo_e0_.number as number2_1_0_, userinfo_e0_.address as address3_1_0_, userinfo_e0_.age as age4_1_0_ from UserInfo_Embededdable userinfo_e0_ where userinfo_e0_.name=? and userinfo_e0_.number=? binding parameter [1] as [VARCHAR] - [name] binding parameter [2] as [INTEGER] - [128] |
|
get::UserInfo_Embededdable(id=UserPK_Embededdable(name=name, number=128), address=korea, age=53) |
||
tip : @Embeddable 을 선언하지 않고 @Id 로 사용한다면 해당클래스의 toString()값이 들어간다 |
||
@Data |
@Data |
|
create table UserInfo_EmbeddedId ( name varchar(255) not null, number integer not null, address varchar(255), age integer, primary key (name, number) )
|
||
Session session = getSessionFactory().getCurrentSession();
|
insert into UserInfo_EmbeddedId (address, age, name, number) values (?, ?, ?, ?) binding parameter [1] as [VARCHAR] - [korea] binding parameter [2] as [INTEGER] - [53] binding parameter [3] as [VARCHAR] - [name] binding parameter [4] as [INTEGER] - [128]
|
|
//select
|
select userinfo_e0_.name as name1_0_0_, userinfo_e0_.number as number2_0_0_, userinfo_e0_.address as address3_0_0_, userinfo_e0_.age as age4_0_0_ from UserInfo_EmbeddedId userinfo_e0_ where userinfo_e0_.name=? and userinfo_e0_.number=? binding parameter [1] as [VARCHAR] - [name] binding parameter [2] as [INTEGER] - [128] |
|
get::UserInfo_EmbeddedId(id=UserPK_EmbeddedId(name=name, number=128), address=korea, age=53) |
||
@Data
|
@Data
|
create table UserInfo_IdClass ( name varchar(255) not null, number integer not null, address varchar(255), age integer, primary key (name, number) )
|
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession();
|
insert into UserInfo_IdClass (address, age, name, number) values (?, ?, ?, ?) binding parameter [1] as [VARCHAR] - [korea] binding parameter [2] as [INTEGER] - [53] binding parameter [3] as [VARCHAR] - [name] binding parameter [4] as [INTEGER] - [128]
|
UserPK_IdClass userPK = new UserPK_IdClass(); |
select userinfo_i0_.name as name1_2_0_, userinfo_i0_.number as number2_2_0_, userinfo_i0_.address as address3_2_0_, userinfo_i0_.age as age4_2_0_ from UserInfo_IdClass userinfo_i0_ where userinfo_i0_.name=? and userinfo_i0_.number=? binding parameter [1] as [VARCHAR] - [name] binding parameter [2] as [INTEGER] - [128] |
get::UserInfo_IdClass(name=name, number=128, address=korea, age=53) |
객체 영속화 세상에서는 연관 관계assocation와 관계relationship에 대한 이해는 필수입니다.
연관 관계에서 반드시 기억할 두 가지는 다중성multiplicity과 방향성directionality입니다
일대일 1:1 |
한 테이블에서 각 레코드는 반드시 다른 테이블 의 레코드 한 개와 관계가 있다. 반대의 경우도 마찬가지다. 다른 테이블의 레코드는 0일 수도 있다. |
자동차 한 대는 오직 한 개의 엔진만 가진다. |
일대다 또는 다대일 1:N, N:1 |
한 테이블에서 각 레코드는 다른 테이블의 0개 또는 그 이상의 레코드와 관계가 있다. |
영화 한 편은 많은 배우를 가진다(일대다) 배우 한 명은 여러 작품에서 연기할 수 있다 (다대일). |
N:N |
양쪽 테이블 모두 각 레코드가 다른 쪽 테이블의 0개 또는 그 이상의 레코드와 관계가 있다. |
|
Car와 Engine의 관계에서 Car의 속성을 질의해서 Engine을 찾아낼수있습니다. car -> engin
Car 클래스와 Owner 클래스의 경우 주어진 Car 객체로 자동차의 주인이 누구인지 알 수 있으며,
Owner 객체로 차주의 자동차가 무엇인지 알 수 있습니다
양방향성 연관 관계를 유지할 수 있도록 Owner 객체에 Car에 대한 참조를 제공하 고, Car 객체에는
Owner에 대한 참조를 제공하고 있습니다.
부모 객체와 자식 객체의 종속성 설정 : 하이버네이트에서는 "부모"객체가 실행되면 "자식" 혹은 "의존" 객체까지 전이되는 연산을 cascade 어트리뷰트로 처리할 수 있다. 이 기능은 모든 종류의 컬렉션과 연관에 적용된다.
타입 |
행위 |
언제 |
CascadeType.DETACH |
엔티티가 Persistence Context에서 제거되면 (엔티티가 분리 될 것입니다)이 작업은 관계에 반영됩니다. |
Finished Persistence Context 또는 entityManager.detach () entityManager.clear () |
CascadeType.MERGE |
엔티티에 업데이트 된 데이터가 있으면이 작업이 관계에 반영됩니다 |
엔티티가 갱신되고 트랜잭션이 완료되거나, entityManager.merge () |
CascadeType.PERSIST |
새로운 엔티티가 데이터베이스에 유지되면이 조치가 관계에 반영됩니다. |
트랜잭션이 끝나거나, entityManager.persist () |
CascadeType.REFRESH |
엔티티가 데이터베이스와 동기화 된 데이터를 가질 때이 조치가 반영됩니다 |
entityManager.refresh () |
CascadeType.REMOVE |
엔터티가 데이터베이스에서 삭제되면 |
entityManager.remove () |
CascadeType.ALL |
위의 조치 중 하나가 JPA 또는 명령에 의해 호출 될 때,이 조치는 관계에 반영됩니다. |
위에 설명 된 명령이나 행동. |
쉽게 말하기 : 객체 상태 전이 타입 (보통 ALL을 사용한다) |
1. FK지정 |
|
@Data |
@Data |
|
create table UserInfo ( seq integer not null, addr varchar(255), height double not null, weight double not null, primary key (seq) )
create table User_OneToOne ( seq integer generated by default as identity, name varchar(255), password varchar(255), info_seq integer, primary key (seq) )
alter table User_OneToOne add constraint FKmn264qa7ngjx89j3u9a2l7ql2 foreign key (info_seq) references UserInfo |
집고 넘어가기 : 자동으로 User_OneToOne Table(부모테이블)에 INFO_SEQ컬럼 생기고 FK를건다 |
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession();
|
insert into UserInfo (addr, height, weight, seq) values (?, ?, ?, ?) binding parameter [1] as [VARCHAR] - [addr] binding parameter [2] as [DOUBLE] - [180.0] binding parameter [3] as [DOUBLE] - [70.0] binding parameter [4] as [INTEGER] - [1]
insert into User_OneToOne (seq, info_seq, name, password) values (null, ?, ?, ?) binding parameter [1] as [INTEGER] - [1] binding parameter [2] as [VARCHAR] - [name] binding parameter [3] as [VARCHAR] - [pwd] |
|
1. PK를 FK지정 (아주 중요함) |
||
@Data
|
||
|
||
@Data
|
||
|
create table User ( USER_SEQ integer not null, name varchar(255), password varchar(255), primary key (USER_SEQ) )
create table UserInfo ( SEQ integer not null, addr varchar(255), height double not null, weight double not null, primary key (SEQ) ) |
|
▼ 실행 |
||
Session session = getSessionFactory().getCurrentSession();
|
call next value for SEQ_USER Sequence value obtained: 100 insert into User (name, password, USER_SEQ) values (?, ?, ?) [1] as [VARCHAR] - [name] [2] as [VARCHAR] - [pwd] [3] as [INTEGER] - [100] insert into UserInfo (addr, height, weight, SEQ) values (?, ?, ?, ?) [1] as [VARCHAR] - [addr] [2] as [DOUBLE] - [180.0] [3] as [DOUBLE] - [70.0] [4] as [INTEGER] - [100] select userinfo0_.SEQ as SEQ1_1_0_, userinfo0_.addr as addr2_1_0_, userinfo0_.height as height3_1_0_, userinfo0_.weight as weight4_1_0_, user1_.USER_SEQ as USER_SEQ1_0_1_, user1_.name as name2_0_1_, user1_.password as password3_0_1_ from UserInfo userinfo0_ left outer join User user1_ on userinfo0_.SEQ=user1_.USER_SEQ where userinfo0_.SEQ=? [1] as [INTEGER] - [100] get(DB) Entity ---> name |
|
|
||
자동으로 부모 테이블의 PK값을 가지고 자신의 FK쪽 값으로 사용한다 @GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "user")) .... @GeneratedValue(generator = "generator") value값은 부모Entity객체 필드명 |
||
집고 넘어가기 : @JoinColumn(name = "USER_SEQ", referencedColumnName="SEQ") referencedColumnName = 자식테이블 컬럼명 (안적으면 자동으로 자식테이블 ID로 해준다.) 또한 PrimaryKeyJoinColumn을 쓸수도 있다 @PrimaryKeyJoinColumn(name = "USER_SEQ", referencedColumnName="SEQ") |
|
|
소유자 찾기
@Data |
@Data
|
|
UserInfo Entity (자식테이블) 에서 부모Entity에서 선언된 자식Entity의 필드명을 적으면된다 @OneToOne(mappedBy = “code feildName”) |
||
집고 넘어가기 : insert했을때 Entity는 관계설정이 되어있지 않아 로그가 찍히지 않는다 하지만 다시 SELECT해온 Entity는 방향성 설정이 되어 넘어온다 여기서 알수 있는것은 insert했을때 사용했던 Entity와 get해온 Entity는 다르다는것을 알수 있다. |
|
|
1:N, N:1, N:N 컬렉션 영속화 (List, Set, Map, Array[])
List
▼ OneToMany조인하기 맵핑 테이블 만들어 조인하기 |
|
@Data |
|
|
|
@Data |
|
create table Auth ( seq integer not null, auth varchar(255), expiry timestamp, primary key (seq) )
create table User ( seq integer generated by default as identity, name varchar(255), password varchar(255), primary key (seq) ) -- 새로운 맵핑 테이블이 생긴다 create table User_Auth ( User_seq integer not null, auths_seq integer not null )
alter table User_Auth add constraint UK_n1upof81pc3xys7s3xrovghof unique (auths_seq)
alter table User_Auth add constraint FKmnns1wp8mo27pxkbbwjl8nuun foreign key (auths_seq) references Auth
alter table User_Auth add constraint FKn1dj4srm089e409sq8vkhogug foreign key (User_seq) references User |
|
|
|
▼맵핑 테이블 이름바꾸기 @Data |
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
insert into Auth (auth, expiry, seq) values (?, ?, ?) binding parameter [1] as [VARCHAR] - [ROLE_ADMIB] binding parameter [2] as [TIMESTAMP] - [Sat Dec 17 14:27:21 KST 2016] binding parameter [3] as [INTEGER] - [100]
insert into Auth (auth, expiry, seq) values (?, ?, ?) binding parameter [1] as [VARCHAR] - [ROLE_USER] binding parameter [2] as [TIMESTAMP] - [Sat Dec 17 14:27:21 KST 2016] binding parameter [3] as [INTEGER] - [101]
insert into User_Auth (User_seq, auths_seq) values (?, ?) binding parameter [1] as [INTEGER] - [1] binding parameter [2] as [INTEGER] - [100]
insert into User_Auth (User_seq, auths_seq) values (?, ?) binding parameter [1] as [INTEGER] - [1] binding parameter [2] as [INTEGER] - [101] |
|
Array
@Data |
|
user.setAuths(Stream.of(authc1,authc2).peek(it->{it.setUser(user);}).toArray(AuthComposite[]::new)); |
|
집고 넘어가기 : Array사용할때에는 @OrderColumn으로 선언해야된다. @OrderColumn은 Index값이 들어가도록 하는것입니다. |
Set
@Data |
|
user.setAuths(Stream.of(authc1,authc2).peek(it->{it.setUser(user);}).collect(Collectors.toSet())); |
Map
@Data } |
|
||
|
|
||
user.setAuths(Stream.of(authc1,authc2).peek(it->{it.setUser(user);}). collect(Collectors.toMap(c-> c.getAuth(),c->c))); |
|
||
|
|
||
▼바로 OneToMany조인하기(맵핑테이블 없음) (자식테이블 인조식별자 + 유니크 인덱스) |
|||
@Data |
|
||
|
|
||
▼자식테이블 FK 컬럼 명바꾸기 |
|||
Data |
|
||
집고 넘어가기 : OneToOne에서는 자기자신 컬럼을 가르켰지만 OneToMany에서는 자식컬럼을 말한다. |
|||
▼PK를 FK지정 (맵핑 테이블 없음) |
||
@Data |
@Data |
|
▼실행 |
||
Session session = getSessionFactory().getCurrentSession();
|
|
|
집고 넘어가기 : (PK를 FK지정) (맵핑 테이블 없음) 는 좀 문제가 있다. 수동으로 KEY값을 맵핑시켜줘다. 자동으로 맵핑할수 있는 방법도 있다. (아래에서 알아보자) @GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "user")) |
||
http://howtodoinjava.com/hibernate/hibernate-one-to-many-mapping-using-annotations/ https://en.wikibooks.org/wiki/Java_Persistence/OneToMany http://www.beingjavaguys.com/2013/09/hibernate-one-to-many-mapping.html http://levelup.lishman.com/hibernate/associations/one-to-many.php |
||
▼PK를 FK지정 (맵핑 테이블 없음) GeneratedValue값으로 KEY값 처리시 문제. |
|||
|
|||
|
|||
|
|||
insert into User (name, password, USER_SEQ) values (?, ?, ?) 12:08:37 TRACE : binding parameter [1] as [VARCHAR] - [name] 12:08:37 TRACE : binding parameter [2] as [VARCHAR] - [pwd] 12:08:37 TRACE : binding parameter [3] as [INTEGER] - [100]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) 12:08:37 TRACE : binding parameter [1] as [TIMESTAMP] - [Sun Dec 18 12:08:36 KST 2016] 12:08:37 TRACE : binding parameter [2] as [INTEGER] - [50] 12:08:37 TRACE : binding parameter [3] as [VARCHAR] - [ROLE_ADMIN]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) 12:08:37 TRACE : binding parameter [1] as [TIMESTAMP] - [Sun Dec 18 12:08:36 KST 2016] 12:08:37 TRACE : binding parameter [2] as [INTEGER] - [51] 12:08:37 TRACE : binding parameter [3] as [VARCHAR] - [ROLE_USER] ERROR : HHH000315: Exception executing batch [org.h2.jdbc.JdbcBatchUpdateException: Referential integrity constraint violation: "FKOU1XVFC0SWVOFR46KFD8KHY1N: PUBLIC.AUTHCOMPOSITE FOREIGN KEY(USER_SEQ) REFERENCES PUBLIC.USER(USER_SEQ) (51)"; SQL statement: |
|||
AuthComposite
authc1 = new AuthComposite(); |
|
||
집고 넘어가기 : 부모Entity와 자식Entity에 관계가 맺어있지만 각각클래스의 ID값에 GeneratedValue 설정했기_문에 각각 시퀀스 값으로 들어간다 여기서 FK제약조건이 걸려 오류가 난것이다. |
|||
save 할때 어떻게 해야될까? 1. 이렇게 해보자 부모Entity는 ID는 GeneratedValue 자식Entity은 GeneratedValue사용하지 않고 자동으로 넘어가도록 기대해보자 |
|||
@Data |
insert into User (name, password, USER_SEQ) values (?, ?, ?) parameter [1] as [VARCHAR] - [name] parameter [2] as [VARCHAR] - [pwd] parameter [3] as [INTEGER] - [100]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) parameter [1] as [TIMESTAMP] - [Sun Dec 18 13:10:36 KST 2016] parameter [2] as [INTEGER] - [null] parameter [3] as [VARCHAR] - [ROLE_ADMIN]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) parameter [1] as [TIMESTAMP] - [Sun Dec 18 13:10:36 KST 2016] parameter [2] as [INTEGER] - [null] parameter [3] as [VARCHAR] - [ROLE_USER] |
||
기대는 했지만 자동으로 넘어가지 않는다. 그저 NULL값이 들어간다 정직하다.. 오류난다 |
|||
2. 이렇게 해보자 (나눠서 save하자) 부모Entity만 save 선행후~ 부모Entity에서 추출된 ID (SEQUENCE) 값을 가지고 자식Entity ID값으로 지정 그뒤 자식Entity를 save해보자 |
|||
AuthComposite authc1 = new AuthComposite(); user.setAuths(Stream.of(authc1,authc2).collect(Collectors.toList())); |
|||
---->null call next value for USER_SEQ Sequence value obtained: 100 HHH000387: ResultSet's statement was not registered Generated identifier: 100, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator ---->100
insert into User (name, password, USER_SEQ) values (?, ?, ?) parameter [1] as [VARCHAR] - [name] parameter [2] as [VARCHAR] - [pwd] parameter [3] as [INTEGER] - [100]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) parameter [1] as [TIMESTAMP] - [Sun Dec 18 13:21:49 KST 2016] parameter [2] as [INTEGER] - [100] parameter [3] as [VARCHAR] - [ROLE_ADMIN]
insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) parameter [1] as [TIMESTAMP] - [Sun Dec 18 13:21:49 KST 2016] parameter [2] as [INTEGER] - [100] parameter [3] as [VARCHAR] - [ROLE_USER]
|
|||
집고 넘어가기 1 : 부모Entity만 save후 자식Entity List를 넣은뒤 다시 save했다 여기서 알수 있듯 save 를 타게되면 바로 시퀀스 값을 가져와 바인딩 시켜주니 그것을 가지고 자식 셋팅후 commit을 하면 그때 가서 자식 Entity에 바인딩된 ID값으로 insert된다. |
|||
집고 넘어가기 2 : PK를 PK복합키로만 구성하는 방법과, 인조식별자+유니크 인덱스로 구성하는것은 아직도 많은 논쟁있다. 하지만 어느정도 대세는 인조식별자+유니크 를 두는것으로 많이 기울었습니다. http://okky.kr/article/257331 |
|||
3. 이렇게 해보자 (자식Entity에 ID값을 부모Entity의 ID값으로 자동 save하자) 베스트! |
|||
@Data |
@Data
|
||
|
|||
AuthComposite
authc1 = new AuthComposite(); User user = new User(); |
|||
insert into User (name, password, USER_SEQ) values (?, ?, ?) 10:44:27 TRACE : binding parameter [1] as [VARCHAR] - [name] 10:44:27 TRACE : binding parameter [2] as [VARCHAR] - [pwd] 10:44:27 TRACE : binding parameter [3] as [INTEGER] - [100] 10:44:27 DEBUG : Executing batch size: 1 10:44:27 DEBUG : insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) 10:44:27 TRACE : binding parameter [1] as [TIMESTAMP] - [Wed Dec 21 10:44:27 KST 2016] 10:44:27 TRACE : binding parameter [2] as [INTEGER] - [100] 10:44:27 TRACE : binding parameter [3] as [VARCHAR] - [ROLE_ADMIN] 10:44:27 DEBUG : Reusing batch statement 10:44:27 DEBUG : insert into AuthComposite (expiry, USER_SEQ, auth) values (?, ?, ?) 10:44:27 TRACE : binding parameter [1] as [TIMESTAMP] - [Wed Dec 21 10:44:27 KST 2016] 10:44:27 TRACE : binding parameter [2] as [INTEGER] - [100] 10:44:27 TRACE : binding parameter [3] as [VARCHAR] - [ROLE_USER] |
|||
객체간 관계만 맺어주면 (변수 값셋팅) 자동으로 부모Entity값을 가지고 자기자신ID값으로 사용하는걸 알수 있다. https://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example-annotation/ |
|||
집고 넘어가기 1 : @MonyToOne 쪽에 @JoinColums 를 사용하여 자기자신 테이블 컬럼명을 지정할수도있다. @ManyToOne(cascade = CascadeType.ALL)
|
지연로딩 FetchType.LAZY (기본값) |
@Data
|
|
Session session = getSessionFactory().getCurrentSession(); //////Fetch
|
18:04:14 DEBUG : select user0_.seq as seq1_1_0_, user0_.name as name2_1_0_, user0_.password as password3_1_0_ from User user0_ where user0_.seq=? 18:04:14 TRACE : binding parameter [1] as [INTEGER] - [100]
18:04:19 DEBUG : select auths0_.USER_SEQ as USER_SEQ4_0_0_, auths0_.AUTH_SEQ as AUTH_SEQ1_0_0_, auths0_.auth as auth2_0_0_, auths0_.AUTH_SEQ as AUTH_SEQ1_0_1_, auths0_.auth as auth2_0_1_, auths0_.expiry as expiry3_0_1_, auths0_.user_seq as user_seq4_0_1_, user1_.seq as seq1_1_2_, user1_.name as name2_1_2_, user1_.password as password3_1_2_ from Auth auths0_ left outer join User user1_ on auths0_.user_seq=user1_.seq where auths0_.USER_SEQ=? 18:04:19 TRACE : binding parameter [1] as [INTEGER] - [100] |
즉시로딩 FetchType.EAGER |
@Data
|
즉시 조인 쿼리가 날라간다. select user0_.seq as seq1_1_0_, user0_.name as name2_1_0_, user0_.password as password3_1_0_, auths1_.USER_SEQ as USER_SEQ4_0_1_, auths1_.AUTH_SEQ as AUTH_SEQ1_0_1_, auths1_.auth as auth2_0_1_, auths1_.AUTH_SEQ as AUTH_SEQ1_0_2_, auths1_.auth as auth2_0_2_, auths1_.expiry as expiry3_0_2_, auths1_.user_seq as user_seq4_0_2_, user2_.seq as seq1_1_3_, user2_.name as name2_1_3_, user2_.password as password3_1_3_ from User user0_ left outer join Auth auths1_ on user0_.seq=auths1_.USER_SEQ left outer join User user2_ on auths1_.user_seq=user2_.seq where user0_.seq=? 18:09:42 TRACE : binding parameter [1] as [INTEGER] - [100] |
@Data |
@Data @ManyToMany(cascade = CascadeType.ALL,mappedBy = "shops")
|
|
|
Session session = getSessionFactory().getCurrentSession(); |
|
|
|
insert into User (name, USER_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [VARCHAR] - [name1] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [100]
insert into CoffeShop (close, name, open, SHOP_SEQ) values (?, ?, ?, ?) 15:53:09 TRACE : binding parameter [1] as [TIMESTAMP] - [null] 15:53:09 TRACE : binding parameter [2] as [VARCHAR] - [shop1] 15:53:09 TRACE : binding parameter [3] as [TIMESTAMP] - [null] 15:53:09 TRACE : binding parameter [4] as [INTEGER] - [100]
insert into CoffeShop (close, name, open, SHOP_SEQ) values (?, ?, ?, ?) 15:53:09 TRACE : binding parameter [1] as [TIMESTAMP] - [null] 15:53:09 TRACE : binding parameter [2] as [VARCHAR] - [shop2] 15:53:09 TRACE : binding parameter [3] as [TIMESTAMP] - [null] 15:53:09 TRACE : binding parameter [4] as [INTEGER] - [101]
insert into User (name, USER_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [VARCHAR] - [name2] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [101] 15:53:09 DEBUG : Executing batch size: 1
insert into User_CoffeShop (users_USER_SEQ, shops_SHOP_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [INTEGER] - [100] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [100]
insert into User_CoffeShop (users_USER_SEQ, shops_SHOP_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [INTEGER] - [100] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [101] 15:53:09 DEBUG : Done inserting collection: 2 rows inserted 15:53:09 DEBUG : Inserting collection: [com.khh.hibernate.c2.join.manytomany.entity.User.shops#101]
insert into User_CoffeShop (users_USER_SEQ, shops_SHOP_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [INTEGER] - [101] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [100]
insert into User_CoffeShop (users_USER_SEQ, shops_SHOP_SEQ) values (?, ?) 15:53:09 TRACE : binding parameter [1] as [INTEGER] - [101] 15:53:09 TRACE : binding parameter [2] as [INTEGER] - [101]
|
|
CoffeShop shopBydb = session.get(CoffeShop.class,100); |
|
select users0_.shops_SHOP_SEQ as shops_SH2_2_0_, users0_.users_USER_SEQ as users_US1_2_0_, user1_.USER_SEQ as USER_SEQ1_1_1_, user1_.name as name2_1_1_ from User_CoffeShop users0_ inner join User user1_ on users0_.users_USER_SEQ=user1_.USER_SEQ where users0_.shops_SHOP_SEQ=? 15:59:43 TRACE : binding parameter [1] as [INTEGER] - [100] 15:59:43 DEBUG : -->name1 15:59:43 DEBUG : -->name2 |
@Data |
|
|
|
@Entity |
|
|
|
@Data |
|
|
|
@Data |
|
create table CoffeShop ( SHOP_SEQ integer not null, close timestamp, name varchar(255), open timestamp, primary key (SHOP_SEQ) )
create table User ( USER_SEQ integer not null, name varchar(255), primary key (USER_SEQ) )
create table UserShop ( SHOP_SEQ integer not null, USER_SEQ integer not null, reg timestamp, primary key (SHOP_SEQ, USER_SEQ) )
alter table UserShop add constraint FKxjfywgcu7nyi1lfdq0elefqx foreign key (SHOP_SEQ) references CoffeShop
alter table UserShop add constraint FK7t3g73sa7nsvtdikuq8lon55 foreign key (USER_SEQ) references User
|
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
|
insert into User (name, USER_SEQ) values (?, ?) 16:55:03 TRACE : binding parameter [1] as [VARCHAR] - [name1] 16:55:03 TRACE : binding parameter [2] as [INTEGER] - [100] 16:55:03 DEBUG : Reusing batch statement 16:55:03 DEBUG : insert into User (name, USER_SEQ) values (?, ?) 16:55:03 TRACE : binding parameter [1] as [VARCHAR] - [name2] 16:55:03 TRACE : binding parameter [2] as [INTEGER] - [101] 16:55:03 DEBUG : Executing batch size: 2 16:55:03 DEBUG : insert into CoffeShop (close, name, open, SHOP_SEQ) values (?, ?, ?, ?) 16:55:03 TRACE : binding parameter [1] as [TIMESTAMP] - [null] 16:55:03 TRACE : binding parameter [2] as [VARCHAR] - [shop1] 16:55:03 TRACE : binding parameter [3] as [TIMESTAMP] - [null] 16:55:03 TRACE : binding parameter [4] as [INTEGER] - [100] 16:55:03 DEBUG : Reusing batch statement 16:55:03 DEBUG : insert into CoffeShop (close, name, open, SHOP_SEQ) values (?, ?, ?, ?) 16:55:03 TRACE : binding parameter [1] as [TIMESTAMP] - [null] 16:55:03 TRACE : binding parameter [2] as [VARCHAR] - [shop2] 16:55:03 TRACE : binding parameter [3] as [TIMESTAMP] - [null] 16:55:03 TRACE : binding parameter [4] as [INTEGER] - [101] 16:55:03 DEBUG : Executing batch size: 2 16:55:03 DEBUG : insert into UserShop (reg, SHOP_SEQ, USER_SEQ) values (?, ?, ?) 16:55:03 TRACE : binding parameter [1] as [TIMESTAMP] - [2016-12-18 16:55:03.706] 16:55:03 TRACE : binding parameter [2] as [INTEGER] - [100] 16:55:03 TRACE : binding parameter [3] as [INTEGER] - [100] 16:55:03 DEBUG : Reusing batch statement 16:55:03 DEBUG : insert into UserShop (reg, SHOP_SEQ, USER_SEQ) values (?, ?, ?) 16:55:03 TRACE : binding parameter [1] as [TIMESTAMP] - [2016-12-18 16:55:03.73] 16:55:03 TRACE : binding parameter [2] as [INTEGER] - [101] 16:55:03 TRACE : binding parameter [3] as [INTEGER] - [100] 16:55:03 DEBUG : Executing batch size: 2 16:55:03 DEBUG : select usershop0_.SHOP_SEQ as SHOP_SEQ1_2_0_, usershop0_.USER_SEQ as USER_SEQ2_2_0_, usershop0_.reg as reg3_2_0_, coffeshop1_.SHOP_SEQ as SHOP_SEQ1_0_1_, coffeshop1_.close as close2_0_1_, coffeshop1_.name as name3_0_1_, coffeshop1_.open as open4_0_1_, user2_.USER_SEQ as USER_SEQ1_1_2_, user2_.name as name2_1_2_ from UserShop usershop0_ left outer join CoffeShop coffeshop1_ on usershop0_.SHOP_SEQ=coffeshop1_.SHOP_SEQ left outer join User user2_ on usershop0_.USER_SEQ=user2_.USER_SEQ where usershop0_.SHOP_SEQ=? and usershop0_.USER_SEQ=? 16:55:03 TRACE : binding parameter [1] as [INTEGER] - [101] 16:55:03 TRACE : binding parameter [2] as [INTEGER] - [100] |
|
집고 넘어가기 : 여기서 알수 있듯. ManyToOne쪽에서 @JoinColumn(name="SHOP_SEQ",referencedColumnName
= "SHOP_SEQ", insertable = false, updatable = false) 관계테이블은 inser와 update가 이루어지면 안되므로 false를 주었다 |
|
▼ 실행 |
|
User userBydb = session.get(User.class,100); |
|
select user0_.USER_SEQ as USER_SEQ1_1_0_, user0_.name as name2_1_0_ from User user0_ where user0_.USER_SEQ=? 09:55:20 TRACE : binding parameter [1] as [INTEGER] - [100] select shops0_.USER_SEQ as USER_SEQ2_2_0_, shops0_.SHOP_SEQ as SHOP_SEQ1_2_0_, shops0_.SHOP_SEQ as SHOP_SEQ1_2_1_, shops0_.USER_SEQ as USER_SEQ2_2_1_, shops0_.reg as reg3_2_1_, coffeshop1_.SHOP_SEQ as SHOP_SEQ1_0_2_, coffeshop1_.close as close2_0_2_, coffeshop1_.name as name3_0_2_, coffeshop1_.open as open4_0_2_ from UserShop shops0_ left outer join COFFESHOP coffeshop1_ on shops0_.SHOP_SEQ=coffeshop1_.SHOP_SEQ where shops0_.USER_SEQ=? 09:55:20 TRACE : binding parameter [1] as [INTEGER] - [100]
|
Session 객체와 관련하여 트랜잭션이 보장되는 캐시다. 이는 세션의 수명이 유지되는 동안이나
컨버세이션conversation 내에서만 가능하다. 1차 캐시는 하이버네이트 프레임워크에서 기본으로 제공한다.
//팩토리 가져오기 SessionFactory factory = d.getSessionFactory();
//세션가져오기 Session session = factory.getCurrentSession(); //트랜젝션 시작 session.beginTransaction();
User user = new User("admin","admin_pwd"); Set<Auth> auths = new HashSet<Auth>(); auths.add(new Auth("fName1","fCode","fVal")); auths.add(new Auth("fName2","fCode","fVal")); auths.add(new Auth("fName3","fCode","fVal")); user.setAuths(auths); session.save(user);
user = new User("user","user_pwd"); auths = new HashSet<Auth>(); auths.add(new Auth("fName1_1","fCode","fVal")); auths.add(new Auth("fName1_2","fCode","fVal")); auths.add(new Auth("fName1_3","fCode","fVal")); user.setAuths(auths); session.save(user); //1차 캐쉬부분 읽어오기 User load_user = (User) session.load(User.class,new Integer(50)); System.out.println(load_user); load_user.setId("admin_after"); session.save(user); //트랜젝션 커밋 session.getTransaction().commit();
//트랜젝션이 닫혔기때문에 아래 부분은 오류남 User after_load_user = (User) session.load(User.class,new Integer(50)); System.out.println(after_load_user); |
Hibernate: drop table T_AUTH if exists Hibernate: drop table T_USER if exists Hibernate: drop table T_USER_PRIVACY if exists Hibernate: drop sequence SEQ_USER Hibernate: create table T_AUTH (FNC_ID integer generated by default as identity (start with 1), fncCode varchar(255), fncName varchar(255), fncValue varchar(255), user_SEQ integer, primary key (FNC_ID)) Hibernate: create table T_USER (SEQ integer not null, ID varchar(255), PWD varchar(255), primary key (SEQ)) Hibernate: create table T_USER_PRIVACY (SEQ integer generated by default as identity (start with 1), AGE integer, NAME varchar(255), primary key (SEQ)) Hibernate: alter table T_AUTH add constraint FK_5wrx276k6vlwxjehoqh6h1rwa foreign key (user_SEQ) references T_USER Hibernate: create sequence SEQ_USER start with 1 Hibernate: call next value for SEQ_USER Hibernate: insert into T_USER (ID, PWD, SEQ) values (?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) Hibernate: insert into T_USER (ID, PWD, SEQ) values (?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) Hibernate: insert into T_AUTH (FNC_ID, fncCode, fncName, fncValue, user_SEQ) values (default, ?, ?, ?, ?) User(seq=50, id=admin, password=admin_pwd, auths=[Auth(fncId=1, fncName=fName2, fncCode=fCode, fncValue=fVal, user=null), Auth(fncId=2, fncName=fName3, fncCode=fCode, fncValue=fVal, user=null), Auth(fncId=3, fncName=fName1, fncCode=fCode, fncValue=fVal, user=null)]) Hibernate: update T_USER set ID=?, PWD=? where SEQ=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Hibernate: update T_AUTH set USER_SEQ=? where FNC_ID=? Exception in thread "main" org.hibernate.SessionException: Session is closed! |
두 번째로 User 객체를 불렀을 때(load) 세션 자체가 가진 캐시에서 해당 객체를 조회한다. 이렇게 데이터베이스를 이용하는 네트워 크 라운드트립roundtrip을 피한다. 세션 캐시는 클래스 타입과 함께 명시되므로 이미 존재하는 인스턴스를 오버라이드할 때 좀 더 주의해야 한다. |
SessionFactory 클래스를 이용하여 전역에서 사용할 수 있다.
그래서 2차 캐시 안에 있는 어떤 데이터라도 애플리케이션 전체에서 사용이 가능하다.
하이버네이트는 EhCache와 InfiniSpan 같은 오픈소스 캐시 라이브러리를 지원한다.
사용자 정의 캐시 라이브러리를 사용하려면 org.hibernate.cache.spi.CacheProvider 인터페이스
관련 라이브러리를 구현하면 된다. 하이버네이트는 기본 옵션으로 EhCache를 2차 캐시 공급자로 사용한다.
캐시 공급자를 연결하려면 hibernate.cache.provider 클래스 속성에 캐시 공급자를 명시한다.
다음은 JBoss의 InifiniSpan을 캐시 공급자로 연결하는 예다.
<hibernate-configuration> <session-factory> .... <!-- Infinispan 캐시 공급자 지정 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.infinispan.InfinispanRegionFactory </property> </session-factory> </hibernate-configuration> |
다양한 캐시 속성을 이용하여 캐싱할 클래스별로 캐싱 정책을 지정할 수 있다. 매 핑 정의 파일의 class 태그 안에 cache 속성을 확인해 보자. |
transactional |
이 전략은 트랜잭션이 가능한 캐시를 지원하는 캐시 공급자를 위해 제공된다. 모 든 캐시 공급자가 트랜잭션이 가능한 캐싱 상품을 가지고 있지 않다는 점에 유의 하자. |
read-only |
갱신할 필요가 없는 영속화 객체에 자주 접근한다면 read-only를 선택한다. 데 이터베이스를 거의 변경하지 않거나 전혀 변경하지 않는다면 이 옵션으로 성능 이 크게 향상할 것이다. |
read-write |
객체를 데이터베이스에서 읽거나 데이터베이스에 쓸 때 이 방법을 사용한다. |
nonstrict-read-write |
객체를 그다지 자주 갱신하지 않을 때 사용한다. |
이 옵션을 전역에서 사용하려면 설정 파일에서 hibernate.cache.default_cache_concurrency_strategy 속성을 설정한다. |
객체 뿐만 아니라 쿼리도 캐싱할 수도 있다. 특정 쿼리를 빈번하게 사용한다면 캐싱하는 것을 추천한다.
이 기능을 사용하려면 hibernate.cache.use_query_cache 속성을 true로 지정한다.
코드에 한 가지를 더 추가해야 하는데, Query.setCacheable() 메소드를 호출해서 Query의
cacheable 속성을 true로 지정해야한다.
Entity 상속으로 처리하는 방법이 3가지 있다.
1. Table-per-Class 전략
2. Table-per-Subclass 전략
3. Table-per-Concrete-Class 전략
기본값 @Inheritance(strategy=InheritanceType.SINGLE_TABLE) |
|||||||
@Data //@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
|
|||||||
|
|||||||
@Data |
@Data |
@Data |
|||||
|
create table USER ( seq integer not null, name varchar(255), age integer, height double, weight double, password varchar(255), address varchar(255), primary key (seq) ) |
||||||
▼실행 |
|||||||
insert into User (name, address, DTYPE, seq) values (?, ?, 'UserOfficePrivacy', ?) 16:31:06 TRACE : binding parameter [1] as [VARCHAR] - [name1] 16:31:06 TRACE : binding parameter [2] as [VARCHAR] - [addr1] 16:31:06 TRACE : binding parameter [3] as [INTEGER] - [50]
insert into User (name, age, height, weight, DTYPE, seq) values (?, ?, ?, ?, 'UserBodyPrivacy', ?) 16:31:06 TRACE : binding parameter [1] as [VARCHAR] - [name2] 16:31:06 TRACE : binding parameter [2] as [INTEGER] - [2] 16:31:06 TRACE : binding parameter [3] as [DOUBLE] - [2.0] 16:31:06 TRACE : binding parameter [4] as [DOUBLE] - [2.0] 16:31:06 TRACE : binding parameter [5] as [INTEGER] - [51] 16:31:06 DEBUG : insert into User (name, password, DTYPE, seq) values (?, ?, 'UserBankPrivacy', ?) 16:31:06 TRACE : binding parameter [1] as [VARCHAR] - [name2] 16:31:06 TRACE : binding parameter [2] as [VARCHAR] - [pwd] 16:31:06 TRACE : binding parameter [3] as [INTEGER] - [52]
|
|||||||
|
|||||||
집고 넘어가기 : DTYPE 저것은 무엇일까 왜 들어가는걸까? 하이버네이트에서 자동으로 어떤 클래스에의해 INSERT가 되었는지 출처를 알수 있도록 DTYPE이라는 컬럼을 만들어 넣어버렸다. 문제를 해결해보자 |
|||||||
▼DTYPE컬럼명을 변경해보자 @DiscriminatorColumn |
|||||||
@DiscriminatorColumn(name="CLASS_TYPE", discriminatorType=DiscriminatorType.STRING) |
|
||||||
▼DTYPE컬럼을 값을 각각 클래스 별로 다르게 지정해보자 @DiscriminatorValue |
|||||||
@Data |
@Data |
@Data |
|||||
|
|||||||
▼DTYPE컬럼을 없에보자 (사용하지 않기) @DiscriminatorFormula |
|||||||
@Data @Inheritance(strategy=InheritanceType.SINGLE_TABLE) |
create table User ( seq integer not null, name varchar(255), age integer, height double, weight double, password varchar(255), address varchar(255), primary key (seq) ) |
||||||
▼실행 |
|||||||
Session session = getSessionFactory().getCurrentSession(); |
|||||||
insert into User (name, address, seq) values (?, ?, ?) 16:40:56 TRACE : binding parameter [1] as [VARCHAR] - [name1] 16:40:56 TRACE : binding parameter [2] as [VARCHAR] - [addr1] 16:40:56 TRACE : binding parameter [3] as [INTEGER] - [50]
insert into User (name, age, height, weight, seq) values (?, ?, ?, ?, ?) 16:40:56 TRACE : binding parameter [1] as [VARCHAR] - [name2] 16:40:56 TRACE : binding parameter [2] as [INTEGER] - [2] 16:40:56 TRACE : binding parameter [3] as [DOUBLE] - [2.0] 16:40:56 TRACE : binding parameter [4] as [DOUBLE] - [2.0] 16:40:56 TRACE : binding parameter [5] as [INTEGER] - [51]
insert into User (name, password, seq) values (?, ?, ?) 16:40:56 TRACE : binding parameter [1] as [VARCHAR] - [name2] 16:40:56 TRACE : binding parameter [2] as [VARCHAR] - [pwd] 16:40:56 TRACE : binding parameter [3] as [INTEGER] - [52]
|
|||||||
집고 넘어가기 : @Inheritance 어노테이션을 엔티티에 명시함으로써 Table-per-class 상속 전략을 정의한다. 이 어노테이션의 경우 strategy 변수를 통해 전략을 지정하는데, 여기서는 SINGLE_TABLE 전략을 사용한다. 그 외에도 InheritanceType에는 TABLE_PER_CLASS와 JOINED 전략이 있다. Table-per-class 전략을 이용할 때 InheritanceType.TABLE_PER_CLASS 값으로 잘못 지정할 수도 있다. 반드시SINGLE_TABLE만 사용하자. Table-perclass 전략에서는 하위 클래스와 관련된 컬럼에는 NOT NULL 제약사항을 선언하지못한다는 점에 주의하자. |
|||||||
Table-per-class 전략에서 구별자 컬럼을 이용하여 구분할 수있었지만
하나의 테이블을 이용하는 대신에 분리된 테이블을 사용하는 방법도 제공한다. @Table-per-subclass
@Inheritance(strategy=InheritanceType.JOINED) |
||
@Data |
||
|
||
Data |
@Data |
@Data |
▼실행 |
||
Session session = getSessionFactory().getCurrentSession(); |
|
|
|
||
|
||
집고 넘어가기 : 부모 Entity의 TABLE을 기준으로 자식 테이블 내용이 들어간다 userBank Entity를 넣을때 보면 부모Entity의 name값을 넣지 않아도 부모 시퀀스가 들어가고 난뒤 자식Entity의 테이블에 내용이 들어간다
부모SEQ ID컬럼과 자식들의 SEQ컬럼은 자동 생성된다.(부모 ID Column 이름으로) |
부모 클래스의 모든 속성이 자식 클래스와 관련된 테이블에 복사된다. (자주 쓰이진 않는다)
@Data |
||
|
||
@Data |
@Data |
@Data |
|
HQL(Hibernate Query Language)에서는 WHERE, ORDER BY, AVG, MAX 등을 SQL처럼 사용할수 있습니다.
HQL은 객체(Entity)를 사용합니다. 테이블을 나타내는 자리에 엔티티 객체 클래스명을 사용해야 합니다.
@Data |
@Data |
|
▼실행 |
||
Session session = getSessionFactory().getCurrentSession(); |
|
|
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ 11:05:18 DEBUG : -->User(seq=1, name=nanme1, password=pwd1) 11:05:18 DEBUG : -->User(seq=2, name=nanme2, password=pwd2)
|
||
▼실행 (where) |
||
Query query = session.createQuery("from User as A where A.seq = 1");
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.USER_SEQ=1 -->User(seq=1, name=nanme1, password=pwd1)
|
||
▼실행 (join) (모든객체 받아오기) Object[] 로받아온다. |
||
Query query = session.createQuery("from User as A,
Office as B where A.seq = 1 AND A.seq = B.seq"); |
||
select user0_.USER_SEQ as USER_SEQ1_1_0_, office1_.OFFICE_SEQ as OFFICE_S1_0_1_, user0_.name as name2_1_0_, user0_.password as password3_1_0_, office1_.addr as addr2_0_1_ from User user0_ cross join Office office1_ where user0_.USER_SEQ=1 and user0_.USER_SEQ=office1_.OFFICE_SEQ 11:10:44 DEBUG : -->User(seq=1, name=nanme1, password=pwd1) 11:10:44 DEBUG : -->Office(seq=1, addr=guro1)
|
||
▼실행 (join) (특정객체 받아오기) |
||
@Data 필드를받을수 있는 생성자를 생성한다 |
Query query = session.createQuery("SELECT new Office
(B.seq,B.addr) from User
as A, Office as B where A.seq = 1 AND A.seq = B.seq");
select office1_.OFFICE_SEQ as col_0_0_, office1_.addr as col_1_0_ from User user0_ cross join Office office1_ where user0_.USER_SEQ=1 and user0_.USER_SEQ=office1_.OFFICE_SEQ 11:15:27 DEBUG : -->Office(seq=1, addr=guro1)
|
|
▼실행 (그룹함수) |
||
Query query = session.createQuery("SELECT MAX(A.seq) from User as A"); Object result = query.getSingleResult();
select max(user0_.USER_SEQ) as col_0_0_ from User user0_
-->2
|
||
▼실행 (그룹함수 복합) |
||
Query query = session.createQuery("SELECT MAX(A.seq), MIN(A.seq), AVG(A.seq), COUNT(*) from User as A"); Object[] results = (Object[]) query.getSingleResult(); |
||
select max(user0_.USER_SEQ) as col_0_0_, min(user0_.USER_SEQ) as col_1_0_, avg(cast(user0_.USER_SEQ as double)) as col_2_0_, count(*) as col_3_0_ from User user0_
11:27:35 DEBUG : -->2 11:27:35 DEBUG : -->1 11:27:35 DEBUG : -->1.5 11:27:35 DEBUG : -->2
|
||
▼HSQL에 변수 바인딩하기 ? 처리 |
||
Query query = session.createQuery("from User as A
where A.seq = ?"); |
||
▼HSQL에 변수 바인딩하기 치환이름 처리 |
||
Query query = session.createQuery("from User as A
where A.seq = :seq"); |
||
▼HSQL에 변수 바인딩하기 in조건 list 처리 |
||
Query query = session.createQuery("from User as A
where A.seq in :seq");
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.USER_SEQ in ( ? , ? , ? , ? , ? ) 15:13:42 TRACE : binding parameter [1] as [INTEGER] - [1] 15:13:42 TRACE : binding parameter [2] as [INTEGER] - [2] 15:13:42 TRACE : binding parameter [3] as [INTEGER] - [3] 15:13:42 TRACE : binding parameter [4] as [INTEGER] - [4] 15:13:42 TRACE : binding parameter [5] as [INTEGER] - [5] |
||
|
||
▼단일결과 가져올때 |
||
query.getSingleResult(); |
||
▼복수결과 가져올때 |
||
query.getResultList(); |
||
▼DELETE문 |
||
session.createQuery("delete UserBio as bio where height=:height"); |
||
object를 하나의 Entity에 속해있는것처럼 할수 있는 방법입니다.
@Data |
@Data |
create table User ( USER_SEQ integer not null, addr varchar(255), country varchar(255), mail varchar(255), zip varchar(255), name varchar(255), primary key (USER_SEQ) ) |
|
▼실행 |
|
Session session = getSessionFactory().getCurrentSession(); |
|
insert into User (addr, country, mail, zip, name, USER_SEQ) values (?, ?, ?, ?, ?, ?) 11:36:54 TRACE : binding parameter [1] as [VARCHAR] - [address] 11:36:54 TRACE : binding parameter [2] as [VARCHAR] - [KOREA] 11:36:54 TRACE : binding parameter [3] as [VARCHAR] - [visualkhh@gmail.com] 11:36:54 TRACE : binding parameter [4] as [VARCHAR] - [08-08] 11:36:54 TRACE : binding parameter [5] as [VARCHAR] - [name] 11:36:54 TRACE : binding parameter [6] as [INTEGER] - [1]
|
Enum사용하기 @Enumerated
별도의 Entity를 만들지 않고 Collection을 테이블로 사용하자.
|
@Data |
|
|
||
▼실행 |
||
Session session = getSessionFactory().getCurrentSession(); |
||
create table User ( USER_SEQ integer not null, name varchar(255), primary key (USER_SEQ) ) 14:28:27 DEBUG :
create table User_cars_enum ( User_USER_SEQ integer not null, cars_enum varchar(255) ) 14:28:27 DEBUG :
create table User_cars_set ( User_USER_SEQ integer not null, model varchar(255) ) 14:28:27 DEBUG :
alter table User_cars_enum add constraint FKnyajuee7ju7fuxvjm01owpi8 foreign key (User_USER_SEQ) references User 14:28:27 DEBUG :
alter table User_cars_set add constraint FK90sgwbja3qhfr5hntsu0sfjmh foreign key (User_USER_SEQ) references User |
|
|
▼테이블명, FK컬럼 명바꾸기. |
||
@ElementCollection @CollectionTable(name="CAR_SET",joinColumns=@JoinColumn(name="CAR_SET_SEQ")) |
|
|
@Temproal
@Temporal(value= TemporalType.DATE) |
@Enumerated
@Enumerated |
몇 개의 레코드만 가져오려면 setMaxResults() 메소드에 한계치와 함께 호출함으 로써 페이지네이션Pagination 기능을 사용할 수 있다.
Session session = getSessionFactory().getCurrentSession(); |
select user0_.USER_SEQ as USER_SEQ1_0_, user0_.name as name2_0_ from User user0_ limit ? offset ? 15:01:19 DEBUG : -->User(seq=31, name=name31) 15:01:19 DEBUG : -->User(seq=32, name=name32) 15:01:19 DEBUG : -->User(seq=33, name=name33) 15:01:19 DEBUG : -->User(seq=34, name=name34) 15:01:19 DEBUG : -->User(seq=35, name=name35) 15:01:19 DEBUG : ----------- select user0_.USER_SEQ as USER_SEQ1_0_, user0_.name as name2_0_ from User user0_ limit ? offset ? 15:01:19 DEBUG : -->User(seq=41, name=name41) 15:01:19 DEBUG : -->User(seq=42, name=name42) 15:01:19 DEBUG : -->User(seq=43, name=name43) 15:01:19 DEBUG : -->User(seq=44, name=name44) 15:01:19 DEBUG : -->User(seq=45, name=name45)
|
집고 넘어가기 : 페이징 처리에 아주 유용하게 사용할수 있다. 각각 DB종류 에따라 limt를 걸지 rownum을걸지 이것또한 hibernate가 알아서 해준다 |
하이버네이트에서는 criteria를 도입하여 필터링의 또 다른 방법을 제공합니다.
Criteria와 Restrictions 클래스를 이용하여 좀더 편하게 필터링을 해보자구요~
https://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/chapters/query/criteria/Criteria.html
@Data |
@Data
|
Session session = getSessionFactory().getCurrentSession(); |
|
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_ where user0_.USER_SEQ=1
select cars0_.USER_SEQ as USER_SEQ3_0_0_, cars0_.seq as seq1_0_0_, cars0_.seq as seq1_0_1_, cars0_.model as model2_0_1_ from Car cars0_ where cars0_.USER_SEQ=? 16:19:28 TRACE : binding parameter [1] as [INTEGER] - [1] |
|
▼ 여러 조건걸기 and |
|
|
|
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_ where user0_.USER_SEQ=1 and user0_.name=? 16:23:00 TRACE : binding parameter [1] as [VARCHAR] - [name_1] |
|
▼ 여러 조건걸기 or |
|
Predicate w1
= builder.equal(studentRoot.get("seq"), 1); |
|
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_ where user0_.USER_SEQ=1 or user0_.name=? 17:13:09 TRACE : binding parameter [1] as [VARCHAR] - [name_1]
|
|
▼ group by |
|
Predicate w1 = builder.equal(studentRoot.get("seq"), 1); |
|
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_ where user0_.USER_SEQ=1 or user0_.name=? group by user0_.name
|
|
▼like |
|
Predicate w3 = builder.like(studentRoot.get("name"), "%name_1%"); //or user0_.name like ? |
|
▼between |
|
Predicate w4 = builder.between(studentRoot.get("seq"), 1,100); // or user0_.USER_SEQ between 1 and 100 |
Criteria에서 EAGER로 list가져올때 복수개의 Entity가 나올수 있다
이에따른 조치로는 2가지 방법이 있다
1.
@OneToMany @Fetch(FetchMode.SELECT) //http://stackoverflow.com/questions/19431021/hibernate-and-criteria-that-return-the-same-items-multiple-times private Collection<Authority> authorities; |
2.
Criteria crit = currentSession.createCriteria(User.class); crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);//조인 쿼리가 날라간다 |
강제로 지연로등 사용 하는 방법 : crit.setFetchMode("authorities",
FetchMode.JOIN); //강제로 지연로딩 사용안함
클래스 레벨에서 엔티티의 쿼리를 사용하기 위해 @NamedQuery 어노테이션를 이용하거나 매핑 파일에 선언할 수 있다.
@Data |
|
@Data |
▼실행 FindAll |
Session session = getSessionFactory().getCurrentSession(); |
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_
|
▼실행 Parameter |
Query query = session.getNamedQuery(User.NQ_FIND_BY_USERNAME); |
select user0_.USER_SEQ as USER_SEQ1_1_, user0_.name as name2_1_ from User user0_ where user0_.name=? 11:53:13 TRACE : binding parameter [1] as [VARCHAR] - [name_4]
|
집고 넘어가기 : NamedQuery 하나만 지정하려면 @NamedQuery(name = User.NQ_FIND_ALL, query = "from User"), public class User {.. |
http://www.journaldev.com/3451/hibernate-named-query-example-namedquery https://www.mkyong.com/hibernate/hibernate-named-query-examples/ |
xml 맵핑 하여 사용하기 |
|
<hibernate-configuration> <session-factory> .... <!--List of
XML mapping files --> hibernate.cfg.xml
|
<hibernate-mapping> query.hbm.xml |
Query query = session.getNamedQuery("MNQ_USER_FINDALL"); |
네이티브 쿼리를 실행하는 기능도 제공한다
HQL이 사용하기 간편한데 왜 네이티브 SQL을 ???
데이터베이스 벤더의 특정 함수 또는 생성에 의존적인 쿼리문이 있을 때 (예 : 통계 업무)
네이티브 쿼리를 사용할수밖에 없다
|
|
@Data |
▼실행 FindAll |
Session session = getSessionFactory().getCurrentSession(); List<User> users = query.getResultList(); |
SELECT * FROM USER |
▼ XML맵핑 |
<hibernate-mapping> |
Groovy Template 이용하여 Dynamic Query 사용하기
<hibernate-mapping> |
|
//groovy template dynamic
query |
|
public String makeGQuery(String template, Map<String, Object> o) throws ScriptException { |