๋ธ๋ก๊ทธ ์ฎ๊ฒผ์ต๋๋ค! ๐ก integer.blog
Tacademy ๊ฐ์๋ฅผ ๋ฃ๊ณ ๋ด์ฉ ์ ๋ฆฌ
๊ฐ์ฌ: ๊น์ํ๋ (ํ ์ฐ์ํํ์ ๋ค ๊ฐ๋ฐ์)์ด ๊ฐ์๋ ๊น์ํ๋์ ๋๊บผ์ด JPA ์ฑ ์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋ณด๊ธฐ ์ ์ ๋ณด๋ฉด ๋ฑ ์ข์ ๊ฐ์๋ผ๊ณ ์๊ฐํ๋ค.
๊ทธ๋ฐ๋ฐ ๊ฐ์ ์๊ฐ์ด ๊ธธ๋ค๋ณด๋ ๋ด์ฉ ์ ๋ฆฌ๋ฅผ ํ์ง ์์ผ๋ฉด ๋จธ๋ฆฟ์์ ๋จ์์์ง ์์์ ๋ด์ฉ์ ์ ๋ฆฌํ๋ค.
ํ์ฌ์์ ํ์ผ๋ฟ ํ๋ก์ ํธ ํ๋ฉด์ ์ด ์ ๋ฆฌ ๊ธ์ ๋ณด๊ณ ๋ ๋ณด๊ณ ๋ ๋ณด๊ฒ ๋์๋ค.
1. SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ ๋ฌธ์ ์
1.1. ๋ฌดํ ๋ฐ๋ณต(CRUD)
์๋์ ๊ฐ์ ์ํฉ์์ private String tel;
ํ๋๊ฐ ์ถ๊ฐ๋๋ฉด ๋ชจ๋ tel๊ณผ ๊ด๋ จ๋ sql์ ๋ค ์์ ํด์ผ ๋๋ค.
public class member {
private String memberId;
private String name;
...
}
INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET ...
1.2. Entity ์ ๋ขฐ ๋ฌธ์
์๋์ ๊ฐ์ ์ํฉ์์ Member๊ฐ์ฒด์ Team, Order, Delivery์ ์ฐ๊ด์ ๋งบ์ด๋จ์๊ฑฐ๋ผ๋ ๋ณด์ฅ์ด ์์ด์ ์๋์ฒ๋ผ ์์ฑํ ์ ์๋ค.
class MemberService {
...
public void process(String id) {
Member member = memberDAO.find(id);
member.getTeam(); // ???
member.getOrder().getDelivery(); // ???
}
}
1.3. ๊ณ์ธตํ ์ํคํ ์ณ์์ ์ง์ ํ ์๋ฏธ์ ๊ณ์ธต ๋ถํ ์ด ์ด๋ ต๋ค.
์์์ ์ฒ๋ผ ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๋ถํ ๋์ด์์ง๋ง ๋ ผ๋ฆฌ์ ์ผ๋ก๋ ๋ถํ ๋์๋์ง ๋ชจ๋ฅธ๋ค.
1.4. SQL์ ์์กด์ ์ธ ๊ฐ๋ฐ์ ํผํ๊ธฐ ์ด๋ ต๋ค.
2. ํจ๋ฌ๋ค์์ ๋ถ์ผ์น
๊ฐ์ฒด vs ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค
๊ฐ์ฒด๋ฅผ ์๊ตฌ ๋ณด๊ดํ๋ ๋ค์ํ ์ ์ฅ์๊ฐ ์์ง๋ง ํ์ค์ ์ธ ๋์์ RDB์ NoSQL ๋ฟ์ด๋ค.
๊ฒฐ๊ตญ ๊ฐ์ฒด๋ฅผ SQL๋ก ๋ณํ(์์ฑ)ํ์ฌ ์ ์ฅํ๊ฒ ๋๋ค.
๊ฐ๋ฐ์๊ฐ SQL ๋งคํผ์ผ์ ๋๋ฌด ๋ง์ด ํ๋ค.
MyBatis Generate ๊ฐ์ ๊ฒฝ์ฐ์ ๋ง๋ค๊ธฐ๋ ํธํ์ง๋ง ์ ์ง๋ณด์๊ฐ ์ ์๋๋ค.
2.1. ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐจ์ด
- ์์
์ฐ๊ด๊ด๊ณ
- ๊ฐ์ฒด์ ์ฐ๊ด๊ด๊ณ์๋ ๋ฐฉํฅ์ฑ์ด ์๋ค.
- ํ ์ด๋ธ์ ์ฐ๊ด๊ด๊ณ๋ ๋ฐฉํฅ์ฑ์ด ์๋ค.
- ๋๋ฌธ์ ๋ณดํต์ ์๋์ ๊ฐ์ด ๊ฐ์ฒด๋ฅผ ํ
์ด๋ธ์ ๋ง์ถ์ด ๋ชจ๋ธ๋งํ๋ค.
class Member { String id; Long teamId; //TEAM_ID FK ์ปฌ๋ผ ์ฌ์ฉ String username; } class Team { Long id; //TEAM_ID PK ์ฌ์ฉ String name; }
- ๊ฐ์ฒด๋ค์ด ๋ชจ๋ธ๋ง์ ์๋์ ๊ฐ๋ค.
- Member์ Team์ ๊ฐ์ด ์๋๋ผ ์ค๋ธ์ ํธ๊ฐ ์๋๊ฒ์ด ๋ ๊ฐ์ฒด์งํฅ์ ์ธ ๊ฒ์ด๋ค.
class Member { String id; Team team; String username; Team getTeam() { return team; } } class Team { Long id; String name; }
- ๊ฐ์ฒด๋ ์์ ๋กญ๊ฒ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ์ํ ์ ์์ด์ผ ํ๋ค.
๋ฐ์ดํฐ ํ์
๋ฐ์ดํฐ ์๋ณ ๋ฐฉ๋ฒ
๊ฐ์ฒด๋ต๊ฒ ๋ชจ๋ธ๋งํ ์๋ก ๋งคํ ์์ ๋ง ๋์ด๋๋ค.
๊ฐ์ฒด๋ฅผ ์๋ฐ ์ปฌ๋ ์ ์ ์ ์ฅํ๋ฏ์ด DB์ ์ ์ฅํ ์๋ ์์๊น?
2.2. ORM?
- Object-relational mapping(๊ฐ์ฒด ๊ด๊ณ ๋งคํ)
- ๊ฐ์ฒด๋ ๊ฐ์ฒด๋๋ก ์ค๊ณ
- ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋๋ก ์ค๊ณ
- ORM ํ๋ ์์ํฌ๊ฐ ์ค๊ฐ์์ ๋งคํ
- ๋์ค์ ์ธ ์ธ์ด์๋ ๋๋ถ๋ถ ORM ๊ธฐ์ ์ด ์กด์ฌ
2.3. JPA๋ ํ์ค ๋ช ์ธ
- JPA๋ ์ธํฐํ์ด์ค์ ๋ชจ์
- JPA ํ์ค ๋ช
์ธ๋ฅผ ๊ตฌํํ 3๊ฐ์ง ๊ตฌํ์ฒด
- Hibernate
- EclipseLink
- DataNucleus
- ์ฐ๋ฆฌ๊ฐ ์ค์ ๋ก ์ฐ๋ ๊ฒ์ Hibernate๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.(JPA๋ ์ธํฐํ์ด์ค)
3. ์ค์ต
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.CreateEntityManagerFactory("hello");
// persistence.xml persistence-unit์ name์ด hello
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// ์์
try {
Member member = new Member();
member.setId(100L);
member.setName("ํ์ ์");
em.persist(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
3.1. ์ฃผ์์
- EntityManagerFactory๋ ํ๋๋ง ์์ฑํด์ ์ดํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ๊ณต์
- EntityManager๋ ์ฐ๋ ๋ ๊ฐ์ ๊ณต์ ํ๋ฉด ์๋๋ค. (์ฌ์ฉํ๊ณ ๋ฒ๋ ค์ผ ํ๋ค.)
- JPA์ ๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์ ์์์ ์คํ
4. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ์๋ ์์ฑํ๊ธฐ
- DDL์ ์ดํ๋ฆฌ์ผ์ด์ ์คํ ์์ ์ ์๋ ์์ฑ
- ํ ์ด๋ธ ์ค์ฌ -> ๊ฐ์ฒด ์ค์ฌ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฉ์ธ(dialect)๋ฅผ ํ์ฉํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ง๋ ์ ์ ํ DDL ์์ฑ
- ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ์ฌ์ฉ! (์ด์ ํ๊ฒฝ์์๋ ๋ค๋ฌ์ด์ ์ฌ์ฉ)
4.1. ์ต์
hibernate.hbm2ddl.auto ์ ์ต์ (value)์ ์๋์ ๊ฒ๋ค๋ก ์ฌ์ฉ
- create: ๊ธฐ์กด ํ ์ด๋ธ ์ญ์ ํ ๋ค์ ์์ฑ (DROP + CREATE)
- create-drop: create์ ๊ฐ์ผ๋ ์ข ๋ฃ์์ ์ ํ ์ด๋ธ DROP
- update: ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ๋ฐ์ (์ด์ DB์ ์ฌ์ฉํ๋ฉด ์๋)
- validate: entity์ table์ด ์ ์ ๋งคํ๋์๋์ง๋ง ํ์ธ
- none: ์ฌ์ฉํ์ง ์์
5. ๋งคํ ์ด๋ ธํ ์ด์
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ด๋ค ์์ผ๋ก ๋งคํ๋ ์ง์ ๋ํ ๋งคํ์ ๋ณด
- @Column
- @Column(name=“EXAMPLE”) ์ต์ ์ ์ฃผ๋ฉด ํ๋์ ๋งคํํ DB์ ์ปฌ๋ผ๋ช ์ ์ง์ ํ๋ค.
- ๊ทธ ์ธ์ insertable, updatable, nullable, unique, length ๋ฑ์ ์ต์ ์ด ์๋ค.
- @Temporal
- ๋ ์ง ํ์ ๋งคํ
- @Temporal(TemporalType.DATE) // ๋ ์ง
- @Temporal(TemporalType.TIME) // ์๊ฐ
- @Temporal(TemporalType.TIMESTAMP) // ๋ ์ง์ ์๊ฐ
- @Enumerated(EnumType.STRING)
- ๋ํดํธ๋
EnumType.ORDINAL
์ธ๋ฐ Enum์ ์ ์๋ ์์๋ฅผ ์ซ์๋ก ๋ฐํํ๋ค. - ๊ทธ๋ฐ๋ฐ ๋ง์ฝ ์์๊ฐ ๋ฐ๋๋ฉด ๋ชจ๋ ๊ฒ ๊ผฌ์ฌ๋ฒ๋ฆฌ๋ฏ๋ก ์ด์์์๋ ์ ๋ ์ฌ์ฉํ๋ฉด ์๋๋ค.
EnumType.STRING
์ต์ ์ ์ฃผ๋ฉด Enum์ ์ ์๋ ๊ธ์๊ฐ ๊ทธ๋๋ก ๋ค์ด๊ฐ๋ฏ๋ก, ์ด ์ต์ ์ด ๊ถ์ฅ๋๋ค.
- ๋ํดํธ๋
- @Lob
- ์ปจํ ์ธ ์ ๊ธธ์ด๊ฐ ๋๋ฌด ๊ธธ ๊ฒฝ์ฐ์ binaryํ์ผ๋ก DB์ ๋ฃ์ ๊ฒฝ์ฐ @Lob์ ์ฌ์ฉํ๋ค.
- CLOB๊ณผ BLOB์ด ์๋ค. CLOB์ Character ํํ์ ๊ธด ์ปจํ ์ธ ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด๊ณ , BLOB์ Binary ํํ์ ๊ธด ์ปจํ ์ธ ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด๋ค.
- @Lob ์ด๋ ธํ ์ด์ ์ String ํ์ ์ ์ฐ๋ฉด CLOB์ด ๋๊ณ , Byte ํ์ ์ ์ฐ๋ฉด BLOB์ด ๋๋ค.
- @Transient
- ์ด ์ปฌ๋ผ์ ๋งคํํ์ง ์๋๋ค.
- ์ปฌ๋ผ์ DB์๋ ์ ์ฅํ์ง ์์ง๋ง ๊ฐ์ฒด์๋ ๋๊ณ ์ถ์ ๋ ์ฌ์ฉ (ex ์์ flag๊ฐ)
5.1. ์๋ณ์ ๋งคํ ์ด๋ ธํ ์ด์
- @Id ์ง์ ๋งคํ
- @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์์ (MySQL์ AUTO INCREMENT)
- @Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํ์ค ์ค๋ธ์ ํธ ์ฌ์ฉ (Oracle)
- @SequenceGenerator ํ์
- @Id @GeneratedValue(strategy = GenerationType.TABLE)
- ํค ์์ฑ์ฉ ํ ์ด๋ธ ์ฌ์ฉ, ๋ชจ๋ DB์์ ์ฌ์ฉ
- @TableGenerator ํ์
- @Id @GeneratedValue(strategy = GenerationType.AUTO)
- ๋ํดํธ
- ๋ฐฉ์ธ(dialect)์ ๋ฐ๋ผ ์์ ๋ฐฉ๋ฒ ์ค ์๋ ์ง์
5.2. ๊ถ์ฅํ๋ ์๋ณ์ ์ ๋ต
- ๊ธฐ๋ณธํค์ ์ ์ฝ ์กฐ๊ฑด: null ์๋๊ณ , ์ ์ผํ๋ฉฐ, ๋ณํ์ง ์๋๋ค.
- ํ์ง๋ง ๋ณํ์ง ์๋ ๊ฒ์ ์๊ธฐ ๋๋ฌธ์(์ฌ์ง์ด ์ฃผ๋ฏผ๋ฒํธ๋) ๋์ฒดํค๋ฅผ ์ฐ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
- ๋์ฒดํค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ Sequence, Auto Increment, ํค ์์ฑ ํ ์ด๋ธ ๋ฑ ๋น์ฆ๋์ค์ ์ ํ ๊ด๊ณ์๋ ๊ฒ์ ์ฐ๋ ๊ฒ์ด ์ข๋ค.
- intํ์ ์ 10์ต~20์ต ์ฌ์ด์์ ๋๋๊ธฐ ๋๋ฌธ์ Long์ ์ฐ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
- ๊ถ์ฅ:
Longํ์ + ๋์ฒดํค + ํค ์์ฑ์ ๋ต
์ฌ์ฉ
6. ์ฐ๊ด๊ด๊ณ ๋งคํ
6.1. ๊ฐ์ฒด๋ฅผ ํ ์ด๋ธ์ ๋ง์ถ์ด ๋ชจ๋ธ๋ง ํ ๊ฒฝ์ฐ
6.1.1. ์ฐธ์กฐ ๋์ ์ ์ธ๋ํค๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉ
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column
private String name;
private int age;
@Column(name = "TEAM_ID")
private Long teamID;
...
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
...
}
6.1.2. ์ธ๋ํค ์๋ณ์๋ฅผ ์ง์ ๋ค๋ฃธ
// ํ ์ ์ฅ
Team team = new Team();
team.setName("TeamA");
em.persist(team);
// ํ์ ์ ์ฅ
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId()); // ์ด ๋ถ๋ถ!
em.persist(member);
6.1.3. ์๋ณ์๋ก ๋ค์ ์กฐํ.
// ์กฐํ
Member findMember = em.find(Member.class, member.getId());
//Member์ Team์ด ์ฐ๊ด๊ด๊ณ๊ฐ ์์
Team findTeam = em.find(Team.class, team.getId());
์ฆ, ๊ฐ์ฒด๋ฅผ ํ
์ด๋ธ์ ๋ง์ถ์ด ๋ชจ๋ธ๋งํ๋ ๊ฒ์ ๊ฐ์ฒด์งํฅ์ ์ธ ๋ฐฉ๋ฒ์ด ์๋๋ค.
๊ฐ์ฒด๋ฅผ ํ
์ด๋ธ์ ๋ง์ถ์ด ๋ฐ์ดํฐ ์ค์ฌ์ผ๋ก ๋ชจ๋ธ๋งํ๋ฉด, ํ๋ ฅ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์๋ค.
- ํ ์ด๋ธ์ ์ธ๋ํค๋ก ์กฐ์ธ์ ์ฌ์ฉํด์ ์ฐ๊ด๋ ํ ์ด๋ธ์ ์ฐพ๋๋ค.
- ๊ฐ์ฒด๋ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํด์ ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ์ฐพ๋๋ค.
- ์ด์ฒ๋ผ ํ ์ด๋ธ๊ณผ ๊ฐ์ฒด๋ ํฐ ๊ฒฉ์ฐจ๊ฐ ์๋๋ฐ, ์์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ด ๊ฒฉ์ฐจ๋ฅผ ๋ฌด์ํ๋ค.
7. ๋จ๋ฐฉํฅ ๋งคํ
7.1. ๊ฐ์ฒด์ ์ฐธ์กฐ(team)์ ํ ์ด๋ธ์ ์ธ๋ํค(TEAM_ID)๋ฅผ ๋งคํ (=์ฐ๊ด๊ด๊ณ ๋งคํ)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column
private String name;
private int age;
//@Column(name = "TEAM_ID")
//private Long teamID;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
...
}
์์ ์ฝ๋์์ ๋ง์ฝ @ManyToOne(fetch = FetchType.LAZY)
๋ฅผ ์ฃผ๋ฉด,
Member ๊ฐ์ฒด๋ง ์กฐํํ๊ณ Team ๊ฐ์ฒด๋ ์ค์ ์ฌ์ฉ๋๋ ์์ ์ DB๋ฅผ ์กฐํํ๋ค. (์ง์ฐ ๋ก๋ฉ
)
๊ทธ๋ ๋ค๊ณ Team ๊ฐ์ฒด๊ฐ null์ด ๋๋ฉด ์๋ฌ๊ฐ ๋๊ธฐ ๋๋ฌธ์ Team ๊ฐ์ฒด๋ ํ๋ก์ ๊ฐ์ฒด(๊ฐ์ง ๊ฐ์ฒด)
๊ฐ ๋ค์ด๊ฐ๋ค.
๋ํดํธ๋ (fetch = FetchType.EAGER)
๋ก ๊ฐ์ด ์กฐํํ๋ค.
๊ถ์ฅํ๋ ๊ฒ์ LAZY
(์ง์ฐ ๋ก๋ฉ)์ด๋ค.
ํ์
์์๋ ์ ๋ถ LAZY๋ก ๋ฐ๋ฅด๊ณ , ๊ผญ ํ์ํ ๊ณณ์์๋ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ์์ ์ ์ํ๋ ๊ฒ์ ๋ฏธ๋ฆฌ ์ต์ ํํด์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ ์ฐ๊ฒ ํ๋ค.
์ฆ, ์๋จํด์ ์ต์ ํํ์ง ๋ง์๋ ๊ฒ์ด๋ค.
7.2. ์ฐ๊ด๊ด๊ณ ์ ์ฅ
// ํ ์ ์ฅ
Team team = new Team();
team.setName("TeamA");
em.persist(team);
// ํ์ ์ ์ฅ
Member member = new Member();
member.setName("member1");
member.setTeam(team); // ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ ์ค์ , ์ฐธ์กฐ ์ ์ฅ
em.persist(member);
7.3. ์ฐธ์กฐ๋ก ์ฐ๊ด๊ด๊ณ ์กฐํ - ๊ฐ์ฒด ๊ทธ๋ํ ํ์
// ์กฐํ
Member findMember = em.find(Member.class, member.getId());
// ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํด์ ์ฐ๊ด๊ด๊ณ ์กฐํ
Team findTeam = findMember.getTeam();
8. ์๋ฐฉํฅ ๋งคํ
8.1. Team ๊ฐ์ฒด์์๋ Member ๊ฐ๋๋ก
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
...
}
8.2. ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ์ฒด ๊ทธ๋ํ ํ์
// ์กฐํ
Team findTeam = em.find(Team.class, team.getId());
// ์ญ๋ฐฉํฅ ์กฐํ
int memberSize = findTeam.getMembers().size();
8.3. ๊ฐ์ฒด์ ํ ์ด๋ธ์ด ๊ด๊ณ๋ฅผ ๋งบ๋ ์ฐจ์ด
- ๊ฐ์ฒด ์ฐ๊ด๊ด๊ณ
- ํ์ -> ํ ์ฐ๊ด๊ด๊ณ 1๊ฐ (๋จ๋ฐฉํฅ)
- ํ -> ํ์ ์ฐ๊ด๊ด๊ณ 1๊ฐ (๋จ๋ฐฉํฅ)
- ํ
์ด๋ธ ์ฐ๊ด๊ด๊ณ
- ํ์ <-> ํ ์ฐ๊ด๊ด๊ณ 1๊ฐ (์๋ฐฉํฅ)
8.4. ๊ฐ์ฒด์ ์๋ฐฉํฅ ๊ด๊ณ
- ๊ฐ์ฒด์ ์๋ฐฉํฅ ๊ด๊ณ๋ ์ฌ์ค ์๋ฐฉํฅ ๊ด๊ณ๊ฐ ์๋๋ผ ์๋ก ๋ค๋ฅธ ๋จ๋ฐฉํฅ ๊ด๊ณ 2๊ฐ์ด๋ค.
- ๊ฐ์ฒด๋ฅผ ์๋ฐฉํฅ์ผ๋ก ์ฐธ์กฐํ๋ ค๋ฉด ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ 2๊ฐ ๋ง๋ค์ด์ผ ํ๋ค.
8.5. ํ ์ด๋ธ์ ์๋ฐฉํฅ ๊ด๊ณ
- ํ ์ด๋ธ์ ์ธ๋ํค ํ๋๋ก ๋ ํ ์ด๋ธ์ ์ฐ๊ด๊ด๊ณ ๊ด๋ฆฌ
- MEMBER.TEAM_ID ์ธ๋ํค ํ๋๋ก ์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ฐ์ง (์์ชฝ์ผ๋ก ์กฐ์ธํ ์ ์๋ค.)
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
8.6. ๊ฐ์ฒด์ ์๋ฐฉํฅ ๊ด๊ณ์์์ ๋ฌธ์ ์
์๋ฅผ ๋ค์ด Member ๊ฐ์ฒด์์ Team ๊ฐ์ฒด์ ๊ฐ์ ๋ณ๊ฒฝ์ํค๊ฑฐ๋,
Team ๊ฐ์ฒด์์ members์ member๋ฅผ ์ถ๊ฐํ๋ ๋ฑ์ ๋ณํ๊ฐ ์์ชฝ์์ ์ผ์ด๋๋ค๋ฉด ์ด๋์ชฝ์ ์ ๋ขฐํด์ผ ํ๋๊ฐ?
๊ทธ๋์ ๋ ์ค ํ๋๋ก ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํด์ผ ํ๋ค.
์ฆ, ํ ์ชฝ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
์ผ๋ก ๋ง๋ค์ด์ฃผ๊ณ ๋๋จธ์ง ํ์ชฝ์ ์กฐํ๋ง ํ๋๋ก ํ๋ ๊ฒ์ด๋ค.
8.7. ์๋ฐฉํฅ ๋งคํ ๊ท์น
- ๊ฐ์ฒด์ ๋ ๊ด๊ณ ์ค ํ๋๋ฅผ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ผ๋ก ์ง์
- ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ๋ง์ด ์ธ๋ํค๋ฅผ ๊ด๋ฆฌ (๋ฑ๋ก, ์์ )
- ์ฃผ์ธ์ด ์๋ ์ชฝ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ
- ์ฃผ์ธ์ mappedBy ์์ฑ ์ฌ์ฉ X
- ์ฃผ์ธ์ด ์๋๋ฉด mappedBy ์์ฑ์ผ๋ก ์ฃผ์ธ ์ง์
8.8. ๋๊ตฌ๋ฅผ ์ฃผ์ธ์ผ๋ก?
- ์ธ๋ํค๊ฐ ์๋ ๊ณณ์ ์ฃผ์ธ์ผ๋ก ์ ํด๋ผ
- ๊ถ์ฅํ๋ ๊ฒ์ ๋จ๋ฐฉํฅ์ผ๋ก ์ค๊ณ๋ฅผ ๋๋ด๊ณ ๊ฐ๋ฐํ๋ฉด์ ์๋ฐฉํฅ์ด ํ์ํ ๋ถ๋ถ์ด ์๊ธฐ๋ฉด ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ์์ ๊ถํ๋ค.
8.9. ์๋ฐฉํฅ ๋งคํ์ ๊ฐ์ฅ ๋ง์ดํ๋ ์ค์
- ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ๊ฐ์ ์ ๋ ฅํ์ง ์๋ ๊ฒ.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//์ญ๋ฐฉํฅ(์ฃผ์ธ์ด ์๋ ๋ฐฉํฅ)๋ง ์ฐ๊ด๊ด๊ณ ์ค์
team.getMembers().add(member);
em.persist(member);
์ด ๊ฒฝ์ฐ, TEAM_ID๊ฐ null์ด ๋๋ค.
ํ์
์์๋ ๊ทธ๋ฅ ์์ชฝ ๋ชจ๋ ๊ฐ์ ์
๋ ฅํ๋ฉด ๋๋ค.
๊ฐ์ฒด์งํฅ ๊ด์ ์์๋ ์์ชฝ ๋ชจ๋ ๊ฐ์ ์
๋ ฅํ๋ ๊ฒ์ด ๋ง๋ค.
8.10. ์๋ฐฉํฅ ๋งคํ์ ์ฅ์
- ๋จ๋ฐฉํฅ ๋งคํ๋ง์ผ๋ก๋ ์ด๋ฏธ ์ฐ๊ด๊ด๊ณ ๋งคํ์ ์๋ฃ
- ์๋ฐฉํฅ ๋งคํ์ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ์กฐํ(๊ฐ์ฒด ๊ทธ๋ํ ํ์) ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋ ๊ฒ ๋ฟ
- JPQL์์ ์ญ๋ฐฉํฅ์ผ๋ก ํ์ํ ์ผ์ด ๋ง์
- ๋จ๋ฐฉํฅ ๋งคํ์ ์ํ๊ณ ์๋ฐฉํฅ ๋งคํ์ ํ์ํ ๋ ์ถ๊ฐํ๋ฉด ๋. (ํ ์ด๋ธ์ ์ํฅ ์์)
9. JPA ๋ด๋ถ ๊ตฌ์กฐ - ์์์ฑ ์ปจํ ์คํธ
JPA์์ ๊ฐ์ฅ ์ค์ํ 2๊ฐ์ง
- ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋งคํํ๊ธฐ
- ์์์ฑ ์ปจํ ์คํธ(PersistenceContext)
9.1. ์์์ฑ ์ปจํ ์คํธ ๋?
- JPA๋ฅผ ์ดํดํ๋๋ฐ ๊ฐ์ฅ ์ค์ํ ์ฉ์ด
์ํฐํฐ๋ฅผ ์๊ตฌ ์ ์ฅํ๋ ํ๊ฒฝ
์ด๋ผ๋ ๋ป- ์์์ฑ ์ปจํ ์คํธ๋ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ๋ (๋์ ๋ณด์ด์ง ์๋๋ค.)
- ์ํฐํฐ ๋งค๋์ ๋ฅผ ํตํด์ ์์์ฑ ์ปจํ ์คํธ์ ์ ๊ทผ
- ์ํฐํฐ ๋งค๋์ ์ ์์์ฑ ์ปจํ
์คํธ๋ 1:1 ์ด๋ผ์ ๊ทธ๋ฅ
EntityManager = PersistenceContext
๋ผ๊ณ ์ดํดํ๋ฉด ๋๋ค.- ์คํ๋ง ํ๋ ์์ํฌ ๊ฐ์ ์ปจํ
์ด๋ ํ๊ฒฝ์์๋ EntityManager์ PersistenceContext๊ฐ
N:1
์ด๋ค.
- ์คํ๋ง ํ๋ ์์ํฌ ๊ฐ์ ์ปจํ
์ด๋ ํ๊ฒฝ์์๋ EntityManager์ PersistenceContext๊ฐ
- ๊ฐ์ ํธ๋์ญ์
์ด๋ฉด ๊ฐ์ ์์์ฑ ์ปจํ
์คํธ์ ์ ๊ทผํ๊ฒ ๋๋ค.
9.2. Entity์ ์๋ช ์ฃผ๊ธฐ
- ๋น์์(new/transient)
- ์์์ฑ ์ปจํ ์คํธ์ ์ ํ ๊ด๊ณ๊ฐ ์๋ ์ํ
- Member ๊ฐ์ฒด๋ฅผ ์์ฑ๋ง ํ ์ํ
//๊ฐ์ฒด๋ฅผ ์์ฑํ ์ํ(๋น์์)
Member member = new Member();
member.setId("memberId");
member.setUsername("ํ์1");
- ์์(managed)
- ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋ ์ํ
- ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ ์ฅํ ์ํ(์์์ฑ ์ปจํ ์คํธ์ ์ํด์ ๊ฐ์ฒด๊ฐ ๊ด๋ฆฌ(managed)๋๋ ์ํ)
Member member = new Member();
member.setId("memberId");
member.setUsername("ํ์1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//๊ฐ์ฒด๋ฅผ ์ ์ฅํ ์ํ(์์)
em.persist(member);
- ์ค์์(detached)
- ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋์๋ค๊ฐ ๋ถ๋ฆฌ๋ ์ํ
em.detach(member);
- ์ญ์ (removed)
- ์ญ์ ๋ ์ํ
em.remove(member);
10. ์์์ฑ ์ปจํ ์คํธ์ ์ด์
10.1. 1์ฐจ ์บ์
- PersistenceContext์๋ ๋ด๋ถ์ 1์ฐจ ์บ์๊ฐ ์๋ค. ์ผ๋ฐ์ ์ธ ์บ์๊ฐ ์๋๋ผ ์์์ฑ์ปจํ ์ค๊ฐ ์์ฑ๋๊ณ ์์ด์ง ๋ ๊น์ง๋ง ์ ๊น ์กด์ฌํ๋ ๊ฒ.
- 1์ฐจ ์บ์์์ ์กฐํ
Member member = new Member();
member.setId("member1");
member.setUsername("ํ์1");
//1์ฐจ ์บ์์ ์ ์ฅ๋
em.persist(member);
//1์ฐจ ์บ์์์ ์กฐํ
Member findMember = em.find(Member.class, "member1");
em.find()
์์ DB๋ก ๋ฐ๋ก๊ฐ๋ ๊ฒ์ด ์๋๋ผ 1์ฐจ ์บ์๋ฅผ ๋จผ์ ํ์ํ๋ค. (์กด์ฌํ๋ฉด ๋ฐ๋ก ๋ฐํ)
- 1์ฐจ ์บ์์ ์์ผ๋ฉด
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ๊ณ
- ์กฐํ๋ ๋ด์ฉ์ 1์ฐจ ์บ์์ ์ ์ฅ ํ์ ๋ฐํ
10.2. ์์ ์ํฐํฐ์ ๋์ผ์ฑ(identity) ๋ณด์ฅ
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member2");
System.out.println(a == b); // ๋์ผ์ฑ ๋น๊ต true -> ์์์ ๋ณด์๋ฏ์ด 1์ฐจ์บ์๊ฐ ์๊ธฐ ๋๋ฌธ์
- 1์ฐจ ์บ์๋ก ๋ฐ๋ณต ๊ฐ๋ฅํ ์ฝ๊ธฐ(REPEATABLE READ) ๋ฑ๊ธ์ ํธ๋์ญ์
๊ฒฉ๋ฆฌ ์์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์๋ ์ดํ๋ฆฌ์ผ์ด์
์ฐจ์์์ ์ ๊ณต
10.3. ํธ๋์ญ์ ์ ์ง์ํ๋ ์ฐ๊ธฐ ์ง์ฐ(transactional write-behind) - ๋ฒํผ ๊ธฐ๋ฅ
- ์๋ฅผ ๋ค์ด
persist(memberA)
๋ช ๋ น์ ๊ฒฝ์ฐ memberA๋ฅผ 1์ฐจ ์บ์์ ์ ์ฅํ๋ฉด์ ๋์์ INSERT SQL์ ์์ฑํด์์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์
์ ๋ง์๋๋๋ค. - ์ดํ์
persist(memberB)
๋ช ๋ น์ ๊ฒฝ์ฐ๋ ์์ ๊ฐ์ด ๋์ํ๊ณ , ์ฌ์ ํ DB์ ๋ฃ์ง ์๋๋ค. - ์ดํ์
commit()
๋ช ๋ น์ ํด์ผ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ์๋ INSERT SQL ์ฟผ๋ฆฌ 2๊ฐ๋ฅผ (์ต์ ์ ๋ฐ๋ผ ๋์์ ํน์ ํ๋์ฉ) DB์ ๋ฃ๋๋ค. ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์
์ ์๋ ์ฟผ๋ฆฌ๋ค์ ๋ ๋ฆฌ๋ ๊ณผ์ ์flush
๋ผ๊ณ ํ๋ค.- ํ์ง๋ง flush๋ฅผ ํ๋ค๊ณ 1์ฐจ์บ์์ ๋ด์ฉ๋ค์ด ์ง์์ง๋ ๊ฒ์ด ์๋๋ผ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ด์ DB์ ์ฑํฌ๋ฅผ ๋ง์ถ๋ ์ญํ ์ ํ๋ค.
commit()
์ด flush์ commit ๋ ๊ฐ์ง ์ผ์ ํ๋ ๊ฒ์ด๋ค.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// ์ํฐํฐ ๋งค๋์ ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์
์ ์์ํด์ผ ํ๋ค.
transaction.begin();
em.persist(memberA);
em.persist(memberB);
//์ฌ๊ธฐ๊น์ง INSERT SQL์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณด๋ด์ง ์๋๋ค.
//์ปค๋ฐํ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ INSERT SQL์ ๋ณด๋ธ๋ค.
transaction.commit(); // [ํธ๋์ญ์
] ์ปค๋ฐ
10.4. ๋ณ๊ฒฝ ๊ฐ์ง(Dirty Checking)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// ์ํฐํฐ ๋งค๋์ ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์
์ ์์ํด์ผ ํ๋ค.
transaction.begin(); // [ํธ๋์ญ์
] ์์
// ์์ ์ํฐํฐ ์กฐํ
Member memberA = em.find(Member.class, "memberA");
// ์์ ์ํฐํฐ ๋ฐ์ดํฐ ์์
memberA.setName("hjs");
memberA.setAge(10);
// ์์ ํ์ผ๋ em.update(member) ์ด๋ฐ ์ฝ๋๊ฐ ์์ด์ผ ํ์ง ์์๊น?
// ํ์ง๋ง ํ์์๋ค. ์ปค๋ฐํ๋ฉด ์๋์ผ๋ก ์
๋ฐ์ดํธ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค.
transaction.commit(); // [ํธ๋์ญ์
] ์ปค๋ฐ
- Dirty Checking์ ๋์ ์๋ฆฌ
- JPA๋ ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๋ ์์ ์ 1์ฐจ ์บ์ ๋ฟ๋ง ์๋๋ผ ์ค๋ ์ท๋ ์์ฑํ๋ค.
- commit()๋ช ๋ น์ผ๋ก flush๋ฅผ ํ๋ฉด ์์์ฑ ์ปจํ ์คํธ์ ์ํด ๊ด๋ฆฌ๋๋ ์ํฐํฐ๋ค์ ์ค๋ ์ท๊ณผ ๋น๊ตํด์ ๋ฐ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด UPDATE ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ DB์ ๋ณด๋ด๊ณ commit์ ํ๋ค.
- ์ด๋ ๊ฒ ํ๋ ์ด์
- Java ์ปฌ๋ ์ ์์ ๊ฐ์ ๊ฐ์ ธ์์ ๋ณ๊ฒฝํด๋ ๋ค์ ์ปฌ๋ ์ ์ ๊ฐ์ ๋ด์ง ์๋๋ค. ๊ทธ๋๋ ์ปฌ๋ ์ ์ ๊ฐ์ด ๋ฐ๋๋ค. ๊ทธ๊ฒ๊ณผ ๋๊ฐ์ ์ปจ์ ์ด๋ค.
- ๋ง์น Java ์ปฌ๋ ์
์์ ๊ฐ์ ๊ฐ์ ธ์์ ๋ณ๊ฒฝํ๋ ๊ฒ ์ฒ๋ผํ๊ธฐ ์ํด ์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๋ค.
10.5. ์ง์ฐ ๋ก๋ฉ(Lazy Loading)
11. Flush
11.1. ์์์ฑ ์ปจํ ์คํธ๋ฅผ flush ํ๋ ๋ฐฉ๋ฒ
- em.flush() - ์ง์ ํธ์ถ
- ํธ๋์ญ์ ์ปค๋ฐ - ํ๋ฌ์ ์๋ ํธ์ถ
JPQL ์ฟผ๋ฆฌ ์คํ - ํ๋ฌ์ ์๋ ํธ์ถ
- JPQL ์ฟผ๋ฆฌ ์คํ์ flush๊ฐ ์๋์ผ๋ก ํธ์ถ๋๋ ์ด์
em.persist(memberA); em.persist(memberB); em.persist(memberC); // ์ค๊ฐ์ JPQL ์คํ query = em.createQuery("select m from Member m", Member.class); List<Member> members = query.getResultList();
- ์ด ์ํฉ์์๋ DB์์ ๋ฐ์ดํฐ ์กฐํ๊ฐ ํ๋๋ ์๋๋ค. flush๊ฐ ์๋์๊ธฐ ๋๋ฌธ์.
- ๋๋ฌธ์ JPA์์๋ JPQL์ ์คํํ๋ฉด flush๊ฐ ์๋์ผ๋ก ํธ์ถ๋๋๋ก ํ๋ค. (MyBatis๋ Spring JDBC์ ํจ๊ป ์ฌ์ฉํ ๋๋ flush๋ฅผ ์ง์ ํด์ค์ผ ํ๋ค.)
11.2. Flush ์ต์
em.setFlushMode(FlushModeType.AUTO)
- ๋ํดํธ- ์ปค๋ฐ์ด๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๋ flush
em.setFlushMode(FlushModeType.COMMIT)
- ์ปค๋ฐํ ๋๋ง flush
- ์ปค๋ฐํ ๋๋ง flush
11.3. Flush๋!
- ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋น์ฐ์ง ์์
- ์์์ฑ ์ปจํ ์คํธ์ ๋ณ๊ฒฝ๋ด์ฉ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๊ธฐํํ๋ ๊ฒ์ด flush์ ๋ชฉ์
- flush๊ฐ ๊ฐ๋ฅํ ์ด์ ๋ DB์ ํธ๋์ญ์ ์ด๋ผ๋ ์์ ๋จ์๊ฐ ์๊ธฐ ๋๋ฌธ. -> ์ปค๋ฐ ์ง์ ์๋ง ๋๊ธฐํํ๋ฉด ๋จ
12. ์ค์์ ์ํ
- ์์ ์ํ์ Entity๊ฐ ์์์ฑ ์ปจํ ์คํธ์์ ๋ถ๋ฆฌ(detached)
- ์์์ฑ ์ปจํ ์คํธ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉ ๋ชปํจ
12.1. ์ค์์ ์ํ๋ก ๋ง๋๋ ๋ฐฉ๋ฒ
- em.detach(entity)
- ํน์ ์ํฐํฐ๋ง ์ค์์ ์ํ๋ก ์ ํ
- em.clear()
- ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์์ ํ ์ด๊ธฐํ
- em.close()
- ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ข ๋ฃ
12.2. ์ค์์ ์ํ๋ฉด ์ง์ฐ ๋ก๋ฉ์ ๋ชป์ด๋ค.
์ง์ฐ ๋ก๋ฉ์ ์ฐ๋ ค๋ฉด ์์์ฑ ์ปจํ
์คํธ๊ฐ ์ด์์์ด์ผ ํ๋ค.
์์์ฑ ์ปจํ
์คํธ๊ฐ ์ฃฝ์ด์๋๋ฐ ์ง์ฐ ๋ก๋ฉ์ด ์ ์ฉ๋ ๊ฐ์ฒด๋ฅผ ํฐ์นํ๋ฉด LazyInitializationException
์๋ฌ๊ฐ ํฐ์ง๋ค.(ํ์
์์ ์์ฃผ ๋ง๋๋ค.)
์์์ฑ ์ปจํ
์คํธ๊ฐ DB์ปค๋ฅ์
๋ฑ์ ๋ค ๋ค๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ทธ๋ ๋ค.
12.3. ํ๋ก์์ ์ฆ์๋ก๋ฉ(EAGER) ์ฃผ์
- ๊ฐ๊ธ์ ์ง์ฐ ๋ก๋ฉ(LAZY)์ ์ฌ์ฉ
- ์ฆ์ ๋ก๋ฉ์ ์ ์ฉํ๋ฉด ์์ํ์ง ๋ชปํ SQL์ด ๋ฐ์
- ์ฆ์ ๋ก๋ฉ์ JPQL์์ N+1 ๋ฌธ์ ๋ฅผ ์ผ์ผํจ๋ค.
- @ManyToOne, @OneToOne์ ๊ธฐ๋ณธ์ด ์ฆ์ ๋ก๋ฉ -> LAZY๋ก ์ค์ ํ ๊ฒ.
- @OneToMany, @ManyToMany๋ ๊ธฐ๋ณธ์ด ์ง์ฐ ๋ก๋ฉ
13. JPQL
- JPA๋ฅผ ์ฌ์ฉํ๋ฉด Entity ๊ฐ์ฒด ์ค์ฌ์ผ๋ก ๊ฐ๋ฐ
- ๋ฌธ์ ๋ ๊ฒ์ ์ฟผ๋ฆฌ
- ๊ฒ์์ ํ ๋๋ ํ ์ด๋ธ์ด ์๋ Entity ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ๊ฒ์
- ๋ชจ๋ DB ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํด์ ๊ฒ์ํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅ
- ์ดํ๋ฆฌ์ผ์ด์ ์ด ํ์ํ ๋ฐ์ดํฐ๋ง DB์์ ๋ถ๋ฌ๋ด๋ ค๋ฉด, ๊ฒฐ๊ตญ ๊ฒ์ ์กฐ๊ฑด์ด ํฌํจ๋ SQL์ด ํ์
๊ทธ๋์ JPA๋ SQL์ ์ถ์ํํ JPQL์ด๋ผ๋ ๊ฐ์ฒด์งํฅ ์ฟผ๋ฆฌ ์ธ์ด ์ ๊ณต.
SQL๊ณผ ๋ฌธ๋ฒ ์ ์ฌ(SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN).
JPQL์ Entity ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ์ฟผ๋ฆฌ. (SQL์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ๋์์ผ๋ก ์ฟผ๋ฆฌ)
// ๊ฒ์
String jpql = "select m From Member m where m.name like '%hello%'"; //Member๋ ๊ฐ์ฒด.
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
JPQL์ ํ
์ด๋ธ์ด ์๋ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ๊ฒ์ํ๋ ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ.
SQL์ ์ถ์ํํด์ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค SQL์ ์์กดํ์ง ์๋๋ค.
JPQL์ ํ๋ง๋๋ก ์ ์ํ๋ฉด ๊ฐ์ฒด ์งํฅ SQL
13.1. JPQL ๋ฌธ๋ฒ
- Entity์ ์์ฑ์ ๋์๋ฌธ์ ๊ตฌ๋ถ
- JPQL ํค์๋๋ ๋์๋ฌธ์ ๊ตฌ๋ถ ์ํจ(SELECT, FROM, where)
- Entity ์ด๋ฆ์ ์ฌ์ฉ, ํ ์ด๋ธ ์ด๋ฆ์ด ์๋
- ๋ณ์นญ์ ํ์
- ๊ฒฐ๊ณผ ์กฐํ API
query.getResultList()
: ๊ฒฐ๊ณผ๊ฐ ํ๋ ์ด์, ๋ฆฌ์คํธ ๋ฐํquery.getSingleResult()
: ๊ฒฐ๊ณผ๊ฐ ์ ํํ ํ๋, ๋จ์ผ ๊ฐ์ฒด ๋ฐํ(์ ํํ ํ๋๊ฐ ์๋๋ฉด ์์ธ ๋ฐ์)
- ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam); //์ด๋ฆ ๊ธฐ์ค (๊ถ์ฅ)
SELECT m FROM Member m where m.username=?1
query.setParameter(1, usernameParam); //์์น ๊ธฐ์ค
- ํ๋ก์ ์
SELECT m FROM Member m
-> ์ํฐํฐ ํ๋ก์ ์ SELECT m.team FROM Member m
-> ์ํฐํฐ ํ๋ก์ ์ SELECT username, age FROM Member m
-> ๋จ์ ๊ฐ ํ๋ก์ ์ - new ๋ช ๋ น์ด: ๋จ์ ๊ฐ์ DTO๋ก ๋ฐ๋ก ์กฐํ
SELECT new jpabook.jpql.UserDTO(m.username,m.age) FROM Member m
- DISTINCT๋ ์ค๋ณต ์ ๊ฑฐ
ํ์ด์ง API
- JPA๋ ํ์ด์ง์ ๋ค์ ๋ API๋ก ์ถ์ํ
- setFirstResult(int startPosition): ์กฐํ ์์ ์์น(0๋ถํฐ ์์)
- setMaxResults(int maxResult): ์กฐํํ ๋ฐ์ดํฐ ์
//ํ์ด์ง ์ฟผ๋ฆฌ String jpql = "select m from Member m order by m.name desc"; List<Member> resultList = em.createQuery(jpql, Member.class) .setFirstResult(10) .setMaxResults(20) .getResultList();
์งํฉ๊ณผ ์ ๋ ฌ
select COUNT(m), //ํ์์ SUM(m.age), //๋์ด ํฉ AVG(m.age), //ํ๊ท ๋์ด MAX(m.age), //์ต๋ ๋์ด MIN(m.age) //์ต์ ๋์ด from Member m
์กฐ์ธ (์ผ๋ฐ ์กฐ์ธ๊ณผ ๋ฌธ๋ฒ์ด ์ฝ๊ฐ ๋ค๋ฆ)
- ๋ด๋ถ ์กฐ์ธ
SELECT m FROM Member m [INNER] JOIN m.team t
- ์ธ๋ถ ์กฐ์ธ
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
- ์ธํ ์กฐ์ธ (= ๋ง ์กฐ์ธ. ์ฐ๊ด๊ด๊ณ ์๊ด ์์ด ์กฐ์ธ)
select count(m) from Member m, Team t where m.username = t.name
- ํ์ด๋ฒ๋ค์ดํธ 5.1๋ถํฐ ์ธํ ์กฐ์ธ๋ ์ธ๋ถ ์กฐ์ธ ๊ฐ๋ฅ
- fetch ์กฐ์ธ
- Entity ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ๋ฒ์ ์กฐํํ๋ ๋ฐฉ๋ฒ
- ๋ณ์นญ์ ์ฌ์ฉํ ์ ์๋ค.
- JPQL:
select m from Member m join fetch m.team
- Member๋ฅผ ์กฐํํ ๋ Team๊น์ง ๊ฐ์ด ๊ฐ์ง๊ณ ์ค๋ ๊ฒ. - SQL:
SELECT M.*, T.* FROM MEMBER T INNER JOIN TEAM T ON M.TEAM_ID=T.ID
- fetch ์กฐ์ธ ์์
String jpql = "select m from Member m join fetch m.team"; List<Member> members = em.createQuery(jpql, Member.class).getResultList(); for (Member member : members) { // fetch ์กฐ์ธ์ผ๋ก Member์ Team์ ํจ๊ป ์กฐํํด์ ์ง์ฐ ๋ก๋ฉ ๋ฐ์ ์ํจ System.out.println("username = " + member.getUsername() + ", " + "teamname = " + member.getTeam().name());
์ฌ์ฉ์ ์ ์ ํจ์ ํธ์ถ
select function('group_concat', i.name) from Item i
- ํ์ด๋ฒ๋ค์ดํธ๋ ์ฌ์ฉ ์ ๋ฐฉ์ธ์ ์ถ๊ฐํด์ผ ํ๋ค.
Named ์ฟผ๋ฆฌ
- ๋ฏธ๋ฆฌ ์ ์ํด์ ์ด๋ฆ์ ๋ถ์ฌํด๋๊ณ ์ฌ์ฉํ๋ JPQL
- ์ด๋ ธํ ์ด์ , XML์ ์ ์
- ์ดํ๋ฆฌ์ผ์ด์ ๋ก๋ฉ ์์ ์ ์ด๊ธฐํ ํ ์ฌ์ฌ์ฉ
- ์ดํ๋ฆฌ์ผ์ด์
๋ก๋ฉ ์์ ์ ์ฟผ๋ฆฌ๋ฅผ ๊ฒ์ฆ
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
public class Member {
...
}
List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "ํ์1")
.getResultList();
14. Spring Data JPA
14.1. Spring Data JPA ์ ์ฉ ์
public class MemberRepository {
public void save(Member member) {...}
public Member findOne(Long id) {...}
public List<Member> findAll() {...} // ๊ณตํต
public Member findByName(String name) {...} // ๊ณตํต ์๋
}
public class ItemRepository {
public void save(Member member) {...}
public Member findOne(Long id) {...}
public List<Member> findAll() {...}
}
์์ ๊ฐ์ด ๊ฒฐ๊ตญ CRUD๊ฐ ๋ฐ๋ณต๋๋ค.
Spring Data JPA๋ ์ง๋ฃจํ๊ฒ ๋ฐ๋ณต๋๋ CRUD ๋ฌธ์ ๋ฅผ ์ธ๋ จ๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ.
๊ฐ๋ฐ์๋ ์ธํฐํ์ด์ค๋ง ์์ฑํ๊ณ , ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ๊ตฌํ ๊ฐ์ฒด๋ฅผ ๋์ ์ผ๋ก ์์ฑํด์ ์ฃผ์
.
14.2. Spring Data JPA ์ ์ฉ ํ
Spring Data JPA๊ฐ ๋ก๋ฉ ์์ ์ ItemRepository๋ฅผ ํ์ธํ๊ณ ๊ตฌํํด๋์ค๋ฅผ ์์ฑํด์ค๋ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
Member findByName(String name);
}
public interface ItemRepository extends JpaRepository<Item, Long> {
// ๋น์ด์์
}
๊ณตํตํ ํ ์ ์์๋ Member findByName(String name);
์ ์ ์ธํ๊ณ ๋ JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ผ๋ฉด ๋์ด๋ค.
์ฆ, JpsRepository ์ธํฐํ์ด์ค๋ ๊ณตํต CRUD๋ฅผ ์ ๊ณตํด์ค๋ค.
์ ๋ค๋ฆญ์ <์ํฐํฐ, ์๋ณ์>
๋ก ์ค์ .
Spring Data JPA๋ Spring Data ํ๋ก์ ํธ์ ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ์ ๊ฒ์ด๋ค.
์คํ๋ง ๋ฐ์ดํฐ JPA์ JpaRepository
์ธํฐํ์ด์ค ->
์คํ๋ง ๋ฐ์ดํฐ์ PagingAndSortingRepository
์ธํฐํ์ด์ค ->
CrudRepository
์ธํฐํ์ด์ค ->
Repository
์ธํฐํ์ด์ค
14.3. ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
๋ฉ์๋ ์ด๋ฆ๋ง์ผ๋ก JPQL ์ฟผ๋ฆฌ ์์ฑ
public interface MemberRepository extends JpaRepository<Member, Long> { List<Member> findByName(String name); //์ด๋ ๊ฒ๋ง ์์ฑํ๋ฉด ์์์ JPQL ์ง์ค }
- ์๋ฅผ ๋ค์ด
List<Member> member = memberRepository.findByName("hello")
์ ๊ฒฝ์ฐ ์คํ๋ SQL์
SELECT*FROM MEMBER M WHERE M.NAME = 'hello'
- ์ด๋ฆ์ผ๋ก ๊ฒ์+์ ๋ ฌ ํ๋ ๊ฒฝ์ฐ
pulbic interface MemberRepository extends JpaRepository<Member, Long> { List<Member> findByName(String name, Sort sort); // Sort๊ฐ ์ด๋ฏธ ๊ตฌํ๋์ด ์๋ค. }
SELECT * FROM MEMBER M WHERE M.NAME = 'hello' ORDER BY AGE DESC
- ์ด๋ฆ์ผ๋ก ๊ฒ์+์ ๋ ฌ+ํ์ด์ง ํ๋ ๊ฒฝ์ฐ
public interface MemberRepository extends JpaRepository<Member, Long> { Page<Member> findByName(String name, Pageable pageable); }
์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ด ์์ฑํ๋ฉด ๋๋ค.
@RequestMapping("/search") Page<Member> search(@RequestParam("name") String name, Pageable pageable) { PageRequest pageRequest = PageRequest.of(1, 10); // ํ์ด์ง ๋ฒํธ, ์ฌ์ด์ฆ return repository.findByName(name, pageRequest); }
- ์๋ฅผ ๋ค์ด
@Query ์ด๋ ธํ ์ด์ ์ผ๋ก ์ฟผ๋ฆฌ ์ง์ ์ ์
public interface MemberRepository extends JpaRepository<Member, Long> { @Query("select m from Member m where m.name = ?1") Member findByName(String name, Pageable pageable); }
14.4. ๋ฐํ ํ์ ์ง์
๋ฐํ ํ์ ์ ์ ํ ์ ์๋ค.
List<Member> findByName(String name); // ์ปฌ๋ ์
Member findByEmail(String email); // ๋จ๊ฑด
14.5. Web ํ์ด์ง๊ณผ ์ ๋ ฌ ๊ธฐ๋ฅ
์ปจํธ๋กค๋ฌ์์ ํ์ด์ง ์ฒ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ๋ฐ์ ์ ์๋ค.
/members?page=0&size=20&sort=name,desc
@RequestMapping(vlaue = "/members", method = RequestMethod.GET)
String list(Pageable pageable, Model model) {...}
14.6. Web ๋๋ฉ์ธ ํด๋์ค ์ปจ๋ฒํฐ ๊ธฐ๋ฅ
์ปจํธ๋กค๋ฌ์์ ์๋ณ์๋ก ๋๋ฉ์ธ ํด๋์ค ์ฐพ์
/members/100
@RequestMapping("/members/{memberId}")
Member member(@PathVariable("memberId") Member member) {
return member;
}
15. QueryDSL
- SQL๊ณผ JPQL์ ์ฝ๋๋ก ์์ฑํ ์ ์๋๋ก ๋์์ฃผ๋ ๋น๋ API
- JPA criteria์ ๋นํด์ ํธ๋ฆฌํ๊ณ ์ค์ฉ์ ์ด๋ค.
- ์คํ์์ค
15.1. SQL, JPQL์ ๋ฌธ์ ์
- SQL, JPQL์ ๋ฌธ์, Type-check ๋ถ๊ฐ๋ฅ
- ํด๋น ๋ก์ง ์คํ ์ ๊น์ง ์๋์ฌ๋ถ ํ์ธ ๋ถ๊ฐ(์ปดํ์ผ ์์ ์ ์ ์ ์๋ค.)
15.2. QueryDSL ์ฅ์
- ๋ฌธ์๊ฐ ์๋ ์ฝ๋๋ก ์์ฑ
- ์ปดํ์ผ ์์ ์ ๋ฌธ๋ฒ ์ค๋ฅ ๋ฐ๊ฒฌ
- ์ฝ๋ ์๋์์ฑ(IDE ๋์)
- ๋จ์ํ๊ณ ์ฌ์: ์ฝ๋ ๋ชจ์์ด JPQL๊ณผ ๊ฑฐ์ ๋น์ท
- ๋์ ์ฟผ๋ฆฌ
15.3. QueryDSL ์ฌ์ฉ
- JPQL
select m from Member m where m.age > 18
- QueryDSL
JPAFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list = query.selectFrom(m)
.where(m.age.gt(18))
.orderBy(m.name.desc())
.fetch();
15.4. QueryDSL - ์กฐ์ธ
JPAQueryFactory query = new JPAQueryFactory(em);
QMember m = QMember.member;
QTeam t = QTeam.team;
List<Member> list = query.selectFrom(m)
.join(m.team, t)
.where(t.name.eq("teamA"))
.fetch();
15.5. QueryDSL - ํ์ด์ง API
JPAQueryFactory query = new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list = query.selectFrom(m)
.orderBy(m.age.desc())
.offset(10)
.limit(20)
.fetch();
15.6. QueryDSL - ๋์ ์ฟผ๋ฆฌ
QueryDSL์ ์ฐ๋ ๊ฐ์ฅ ํฐ ์ด์ ๋ ๋์ ์ฟผ๋ฆฌ ๋๋ฌธ
String name = "memebr";
int age = 9;
QMember m = QMember.member;
BooleanBuildere builder = new BooleanBuilder();
if (name != null) {
builder.and(m.name.contains(name));
}
if (age != 0) {
builder.and(m.age.gt(age);
}
List<Member> list = query.selectFrom(m)
.where(builder)
.fetch();
ํนํ DTO๋ก ๋ฐ๋ก ์กฐํํด์ผ ํ๋ ๊ฒฝ์ฐ QueryDSL์ด ํนํ ์ ์ฉํ๋ค.
15.7. QueryDSL - ์ด๊ฒ์ ์๋ฐ๋ค!
return query.selectFrom(coupon)
.where(
coupon.type.eq(typeParam),
coupon.status.eq("LIVE"), //์๋น์ค ํ์ ์ ์ฝ์กฐ๊ฑด
marketing.viewCount.lt(marketing.maxCount) //์๋น์ค ํ์ ์ ์ฝ์กฐ๊ฑด
)
.fetch();
์์ ๊ฐ์ ์ํฉ์์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฝ์กฐ๊ฑด์ ์กฐ๋ฆฝํ ์ ์๋ค. (๊ฐ๋ ์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ ์ ๊ณ )
return query.selectFrom(coupon)
.where(
coupon.type.eq(typeParam),
isServiceable()
)
.fetch();
private BooleanExpression isServiceable() {
return coupon.status.eq("LIST") //์๋น์ค ํ์ ์ ์ฝ์กฐ๊ฑด
.and(marketing.viewCount.lt(marketing.maxCount)); //์๋น์ค ํ์ ์ ์ฝ์กฐ๊ฑด
}