JPA relation annotations and search method generation - TheOpenCloudEngine/uEngine-cloud GitHub Wiki
์ด๋ฒ์๋ Clazz(๊ฐ์) ํด๋ ์ค๋ฅผ ๋ง๋ค์ด์ Course(์ฝ์ค) ์์ ์ฐ๊ฒฐ๊ณ ๋ฆฌ๋ฅผ JPA์์ ๋ฅผ ํตํด์ ์์๋ณด๋๋ก ํ๋ค.
@Entity
@Table(name="CLASS")
public class Clazz {
@Id
@GeneratedValue
Long id;
@ManyToOne @JoinColumn(name="COURSE_ID")
Course course;
@Column(name = "TITLE")
String title;
}
๊ฐ์๋ ์ด๋ค ๊ณผ์ ์ ๊ฐ์์ธ์ง ํ์
ํ๊ธฐ ์ํ์ฌ Course๋ฅผ ๋ณ์๋ก ์ค์ ํ๋ค.
Clazz ์
์ฅ์์ ๋ณด๋ฉด Course ๋ ManyToOne ์ด๋ค. Clazz๋ ์ฌ๋ฌ๊ฐ ์ธ๋ฐ Course๋ 1๊ฐ ๋ผ๋ ๋ง์ด๋ค.
๋ฐ๋๋ก Course ์
์ฅ์์ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ ์ ์๋ค.
@OneToMany(mappedBy = "course")
List<Clazz> clazzList;
Course course๋ฅผ @ManyToOne
๋ก ์ค์ ์ ํ์์ง๋ง, ์ค์ DBํ
์ด๋ธ๋ก ์๊ฐ์ ํ์์๋, ํด๋น ํ
์ด๋ธ๊ณผ ๋งค์นญ์ ํ ์ ์๋
FK(Foreign Key)๋ฅผ ๋ช
์ ํด์ค์ผ ํ๋ค. ์๋์ผ๋ก ์์ฑ์ ํด ์ค์๋ ์์ง๋ง, Course rootCourse; ๊ฐ์ด ์ถ๊ฐ๋ก ๊ด๊ณ๊ฐ ์์ฑ๋ ์ ์์ผ๋
@JoinColumn(name="COURSE_ID")
๊ณผ ๊ฐ์ด ๋ช
์์ ์ผ๋ก Column์ด๋ฆ์ ๋ฃ์ด ์ฃผ์ด์ผ ํ๋ค.
๋ํ java์์๋ reserved keyword ๊ท์น๋๋ฌธ์ Clazz
๋ผ๋ ์ฉ์ด๋ฅผ ์ผ์ง๋ง,
DB์์๋ class
๊ฐ ์์ฝ์ด๊ฐ ์๋๊ธฐ ๋๋ฌธ์
@Table(name="CLASS")
๋ก ๋ช
๋ช
ํ์ฌ class๋ผ๋ ํ
์ด๋ธ์ ์์ฑ ํ ์ ์๋ค.
DB์ฉ์ด(ํ ์ด๋ธ๋ช , ์ปฌ๋ผ๋ช ๋ฑ)๋ ๋๋ฌธ์๋ก ์ฐ๋๊ฒ์ด ์ฝ๋ฉ ๊ท์น์ด๋ค.
Course.java ์์ (mappedBy = "course")
์ ์๋ฏธ๋
Clazz์ course ๋ผ๊ณ ํ๋ java field๊ฐ ๋๋ฅผ ID๋ก ๋ฌผ๊ณ ์๋ค ๋ผ๋ ๊ฒ์ ์๋ฏธํ๋ค.
JoinColumn์ ์ปฌ๋ผ์ ๋ช ์นญ์ ์ฃผ๋ ๊ฑฐ๊ณ , mappedBy๋ java class์ ํ๋๋ช mappedBy๋ ์๋ต ํ ์๋ ์์ง๋ง framework์ ๋ฐ๋ผ์ ์ธ์์ ๋ชปํ๋ ๊ฒฝ์ฐ๋ ์๊ธฐ๋
๋ช ์์ ์ผ๋ก ์ ์ธ์ ํด์ฃผ๋ ๊ฒ์ ์ถ์ฒํ๋ค. spring-boot๊ฐ Hibernate๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๊ณ ์๊ธฐ์, ์ฌ๊ธฐ์๋ Hibernate๋ฅผ ๊ธฐ์ค์ผ๋ก ์ค๋ช ํ๋ค.
ManyToOne ๊ณผ OneToMany๋ฅผ ์ดํด ๋ณด์๋ค.
๋ค๋๋ค ๊ด๊ณ์์๋ ์ค๊ฐ์ Table์ด ํ๋๊ฐ ํ์ํ๋ค.
ManyToMay์์ ์ฌ๊ธฐ์ ์์ธํ ์์ ๋ฅผ ์ดํด ๋ณด์๊ธธ ๋ฐ๋๋ค.
์ ์ ์ค๋ช
์ ๋๋ฆฌ๋ฉด Post์ Tag๋ฅผ ๋ฌถ๊ธฐ ์ํ์ฌ Post_tagํ
์ด๋ธ์ ์์ฑํ์๊ณ ,
๋ ํ
์ด๋ธ์ ๊ด๊ณ๋ฅผ JoinTable ๊ณผ mappedBy๋ก ์ค์ ์ ํ์๋ค.
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
@ManyToMany(mappedBy = "tags")
private List<Post> posts = new ArrayList<>();
๋ฐ์ดํฐ๋ฅผ ์กฐํํ ์ ์ ํน์ ์กฐ๊ฑด์ผ๋ก ๊ฒ์์ ํ๋๊ฒ์ ๋น์ฐํ ์๊ตฌ์ฌํญ์ด๋ค.
์๋ ์ค๋ช
ํ ๋ฐฉ๋ฒ์ JPA ๋ฐฉ์์ ์๋๊ณ Spring-data์์ ์ฐ๋ ๋ฐฉ์์ด๋ค.
Spring-data-jpa๋ ์ด ๊ฒ์ ์กฐ๊ฑด์ naming ๊ท์น์ ์ํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ์๊ณผ queryํ์์ผ๋ก ์ฌ์ฉ๊ฐ๋ฅํ jpql๋ฐฉ์์ ์ฌ์ฉํ์ฌ
์ผ๋ฐ์ ์ธ ํจํด๊ณผ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ๋ชจ๋ ์ง์ํ๋ค.
์ฐ์ ์๋ ์์ ์์๋ ๋ค์ด๋ฐ ๊ท์น์ ์ํ ์ผ๋ฐ์ ์ธ ํจํด์ ์กฐํํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช
ํ๋ค.
public interface CourseRepository extends PagingAndSortingRepository<Course, Long> {
// 1๋ฒ ๋ฐฉ๋ฒ
List<Course> findByTitle(@Param("title") String title);
// 2๋ฒ ๋ฐฉ๋ฒ
List<Course> findByTitleContaining(@Param("title") String title);
}
์ด๋ฆ์์ ์ ์ ์๋ฏ์ด 1๋ฒ ๋ฐฉ๋ฒ์ Title์ ๊ฒ์ํ์ง๋ง, Title์ด ์์ ํ ๊ฐ์๋ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๊ณ ,
2๋ฒ ๋ฐฉ๋ฒ์ Title์ค ์ผ๋ถ ๋จ์ด๊ฐ ํฌํจ ๋์ด์๋ ๊ฒ์ ์กฐํํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
2๋ฒ ๋ฐฉ๋ฒ์ด 1๋ฒ ๋ฐฉ๋ฒ์ ํฌํจํ๊ณ ์์ผ๋ 2๋ฒ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ๋๊ฒ ๋ค.
์ด์ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง ์ดํด ๋ณด๊ฒ ๋ค.
์ฐ์ sample ๋ฐ์ดํฐ๋ฅผ insertํ๋ค.
$ http localhost:8080/courses title="MSA์ค์ต" duration=5 maxEnrollment=5 minEnrollment=10
$ http localhost:8080/courses title="MSA์ด๋ก " duration=5 maxEnrollment=5 minEnrollment=10
$ http localhost:8080/courses title="MSA๊ฐ์" duration=5 maxEnrollment=5 minEnrollment=10
๊ทธ ํ http localhost:8080/courses ๋ผ๋ root๋ก ์กฐํ๋ฅผ ํ๋ฉด search๋ผ๋ ๊ฒ์ด ์๋กญ๊ฒ ์๊ฒผ๋ค.
$ http localhost:8080/courses
"_links": {
"profile": {
"href": "http://localhost:8080/profile/courses"
},
"search": {
"href": "http://localhost:8080/courses/search"
},
"self": {
"href": "http://localhost:8080/courses{?page,size,sort}",
"templated": true
}
},
HATEOAS ์์๋ ์ฌ์ฉ๋ฐฉ๋ฒ์ ์๊ธฐ๊ฐ ์๋ ค์ค๋ค.
$ http http://localhost:8080/courses/search
{
"_links": {
"findByTitle": {
"href": "http://localhost:8080/courses/search/findByTitle{?title}",
"templated": true
},
"findByTitleContaining": {
"href": "http://localhost:8080/courses/search/findByTitleContaining{?title}",
"templated": true
},
"self": {
"href": "http://localhost:8080/courses/search"
}
}
}
## ๊ฒ์ ํ์ธ
$ http http://localhost:8080/courses/search/findByTitleContaining\?title="์ค์ต"