ํด๋น ๊ธ์ ๋ฐฐ์๋ณด์ Spring Data JPA ์๋ฆฌ์ฆ ์ ๋๋ค.
ํด๋น ์๋ฆฌ์ฆ์ ๋ด์ฉ์ด ์ด์ด์ง๋ ํํ์ด๋ฏ๋ก ๊ธ์ ๋ด์ฉ ์ค์ ์๋ต๋๋ ๋ง๋ค์ด ์์ ์ ์์ผ๋, ์์ธํ ์ฌํญ์ ์๋ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์!
- Spring Data JPA์ ๊ธฐ๋ณธ๊ณผ ํ๋ก์ ํธ ์์ฑ :: ์๋ฆฌ์ฆ ํ์ต ํ๊ฒฝ ์ค๋น
- JPA์ ๊ธฐ๋ณธ๊ณผ Spring Data JPA
- Springboot project ์์ JPA ์ค์ ํ๊ธฐ
- JPA์ ๊ธฐ๋ณธ ์ด๋
ธํ
์ด์
๋ค :: JPA์ ์์๊ณผ ๋์์ ๋
- ์ํฐํฐ์ ํ ์ด๋ธ ๋งคํ
- ํ๋์ ์ปฌ๋ผ ๋งคํ
- ๋งคํ ํ
์ด๋ธ๊ณผ ์ฐ๊ด๊ด๊ณ ๋งคํํ๊ธฐ :: RDB์ ๊ฝ, ์ฐ๊ด ๊ด๊ณ
- ์ฐ๊ด๊ด๊ณ๋?, ์ธ๋ ํค๋?, ๋งคํ ํ ์ด๋ธ์ด๋?
- ์ผ๋์ผ, ์ผ๋๋ค, ๋ค๋์ผ ์ฐ๊ด๊ด๊ณ
- ๊ณตํต ์ธํฐํ์ด์ค ๊ธฐ๋ฅ :: ์ด๋ป๊ฒ Data JPA ๋ ๋์ํ ๊น?
- ๋จ๊ฑด ์กฐํ ๋ฐํ ํ์
- ์ปฌ๋ ์ ์กฐํ ๋ฐํ ํ์
- ์ฌ์ฉ์ ์ ์ ์ฟผ๋ฆฌ ์ด์ฉํ๋ ๋ฐฉ๋ฒ :: ๋ด๊ฐ ์ํ๋ ์ฟผ๋ฆฌ๋ฅผ JPA ์์ ๋ง๋ค์ด๋ณด์!
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑํ๊ธฐ
@Query
๋ฅผ ์ด์ฉํ์ฌ ๋ฉ์๋์ ์ ์ ์ฟผ๋ฆฌ ์์ฑํ๊ธฐ
- ํ์ด์ง๊ณผ ์ ๋ ฌ :: ๊ฒ์ํ๊ณผ ๊ฐ์ ํ์ด์ง๊ฐ ์๋ ์๋น์ค์์ ๋น์ ๋ฐํ๋ JPA ํ์ด์ง!
- Data JPA์ ํ์ด์ง๊ณผ ์ ๋ ฌ
- Web MVC ์์ JPA ํ์ด์ง๊ณผ ์ ๋ ฌ
- Auditing :: ๋ชจ๋ ์์ฒญ๊ณผ ์๋ต์
๋๊ฐ, ์ธ์
์ ๊ทผํ๋์ง ํ๋์ ์ํฐํฐ๋ก ๊ด๋ฆฌํ์.- ์์ JPA์ Auditing
- Spring Data JPA์ Auditing
- ๋ฐฐ์๋ณด์ Spring Data JPA ์๋ฆฌ์ฆ๋ฅผ ๋ง์น๋ฉฐ...
- ์๋ฆฌ์ฆ๋ฅผ ๋ง์น๋ฉฐ ๋๋์
- ๋ด๊ฐ ์ ๋ณด๋ฅผ ์ป์ ๊ณณ
- ํด๋น ์๋ฆฌ์ฆ๋ฅผ ์์ฃผํ์ จ๋์?
์์ JPA๋ฅผ ์ด์ฉํ JPA ํ๋ก๊ทธ๋๋ฐ์ ํ๊ณ
์! ์ฐ๋ฆฌ๋ ์ด์ JPA์ ๊ธฐ๋ณธ ์ด๋ ธํ ์ด์ ๋ค์ ๋ํด์ ์ด์ ๋ฐฐ์ ๋ค.
๋ง์ฝ ์ฐ๋ฆฌ๊ฐ ์์ JPA๋ฅผ ์ด์ฉํ์ฌ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
JPA๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋์ ํธ๋์ญ์ ์์์ ๊ธฐ๋ฅ์ ์ํํ๊ธฐ ๋๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ด ํธ๋์ญ์ ์ ์ ์ธํด์ค์ผ ํ๋ค.
์ฝ๋๋ฅผ ๋ณด๊ณ ๊ฒ๋จน์ง ๋ง์!
์ด๋ฐ ๋ฌด์์ด ์ฝ๋๋ค์๊ฒ ์ธ ์ ๊ฒฝ์ ๋ค๋ฅธ ๊ณณ์์ ์ฐ๊ธฐ ์ํด์ Spring Data JPA๊ฐ ๋ฑ์ฅํ ๊ฒ์ด๋ ์๋์ ๋์ฌ ์ฝ๋๋ค์ ๋จ์ง JPA๋ฅผ ์ฌ์ฉํ๋ ค๋ ๋ชฉ์ ์์๋ ์ฌ์ฉํ์ง ์๋๋ค.
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Factory");
// db connection
EntityManager manager = factory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
// ํธ๋์ญ์
์์
transaction.begin();
try {
Member member = new Member();
member.setUsername("Steven Gerrard");
manager.persist(member); // db ์ ์ฅ
Member findMember = manager.find(Member.class, member.getId()); // select ์ฟผ๋ฆฌ
System.out.println(findTeam.getName());
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
manager.close();
factory.close();
}
}
์ด๋ฐ ํํ์ ๊ฐ๋ฐ์ ํด์ผ ํ๋ค.
์ด๊ฑธ Spring ์์ ์ฌ์ฉํ ์ ์์๊น?
์.. ํ๋ ค๋ฉด ํ๊ฒ ์ง๋ง ๋ณดํต์ ์์ JPA๋ฅผ ์ด์ฉํ ๊ฒฝ์ฐ์ ๋ค์๊ณผ ๊ฐ์ด Repository๋ฅผ ์ ์ธํด์ฃผ๊ณ ํน์ ๊ธฐ๋ฅ์ Repository ์์ ๊ตฌํํ์ฌ ์ฌ์ฉํ๋ค.
Repository
์ฐ๋ฆฌ๊ฐ CRUD๋ฅผ ์ํํ์ฌ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๊ธฐ ์ํด์๋ ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ธ Repository ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค.
์์์๋ member์ ๊ธฐ๋ณธ ํค์ธ id๋ก ์กฐํ๋ฅผ ํ๋ฏ๋ก em.find
๋ฅผ ์ฌ์ฉํ ์ ์๋๋ฐ, ์ฌ์ฉ์ ์ด๋ฆ์ผ๋ก ์กฐํํ๋ค๊ณ ํ๋ฉด ์ฐ๋ฆฌ๋ ์๋ก์ด ๋ฉ์๋๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค.
@Repository
public class MemberRepository {
@PersistenceContext;
EntityManager em;
public void save(Member member) {
em.persist(member);
}
public void findByName(String name) {
return em.createQuery("SELECT m FROM Member m WHERE m.name = :name", Member.class)
.setParameter("name", name)
.getResult();
}
}
๊ทธ๋ผ ์ค๋ณต ํ์ ๊ฒ์ฌ๊ฐ ์ถ๊ฐ๋๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
@Repository
public class MemberRepository {
@PersistenceContext;
EntityManager em;
public void save(Member member) {
em.persist(member);
}
public void findByUsername(String name) {
return em.createQuery("SELECT m FROM Member m WHERE m.name = :name", Member.class)
.setParameter("name", name)
.getResult();
}
public void validateUsername(Member member) {
List<Member> findMembers = memberRepository.findByUsername(member.getUsername());
if(!findMembers.isEmpty()) {
throw new IllegalStateException("์ด๋ฏธ ์กด์ฌํ๋ ํ์");
}
}
}
์ด๋ฐ ํํ๊ฐ ๋๋ค.
๋ฌผ๋ก JpaRepository๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ํน์ํ ๊ธฐ๋ฅ์ ์ํด์ ์ถ๊ฐ์ ์ธ ์์ ์ ํด์ผ ํ๋ ๋ฉ์๋๊ฐ ๋ ๋ง์์ง ๊ฒ์ธ๋ฐ, ์์ ์ฝ๋๋ ์ด์ฉ๋ฉด ๊ฐ๋ ์ฑ์ด ์ ์ข์ ๋ณด์ด๊ธฐ๋ ํ๋ค.
๋ญ์ง? ๋ํต ์ฒ์ ๋ณด๋ ๋ฌธ์๋ค์ด ์๋ค.
SQL๋ ์๋๊ฒ ๊ฐ์๋ฐ ํํ๋ SQL ์ด๊ณ .. ์ ํํ๋ JPQL ์ด๋ผ๊ณ ํ๋๋ฐ, ์ฌ์ฉ์ ์ ์ ์ฟผ๋ฆฌ๋ฅผ ์จ์ผํ ๋ ์ฌ์ฉํ๋ค. ์ด๋ ๋ค์์ ๋ฐฐ์ฐ๋ ๊ฑฑ์ ๋ง์.
๋ณธ๋ก ์ผ๋ก ๋์์์.
์์ ๊ณ์ ์ธ๊ธํ์๋ฏ, JPA๋ Spring์ ์ข ์์ ์ธ ๊ฒ์ด ์๋๋ค.
๊ทธ๋์ ์์ ๋ณธ ์ฝ๋๋ค์๋ Spring๊ณผ JPA ์ฌ์ด์ ์ฐจ์ด๊ฐ ์กด์ฌํ๋๋ฐ, ์ด๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ๋ฑ์ฅํ ๊ฒ์ด ๋ฐ๋ก Spring Data JPA ์ธ ๊ฒ์ด๋ค.
Spring Data JPA์ ๊ณตํต ์ธํฐํ์ด์ค
Spring Data JPA ๋ ๊ฐ๋จํ CRUD ๊ธฐ๋ฅ์ ๊ณตํต์ผ๋ก ์ฒ๋ฆฌํ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
์ด๋ฅผ JPA ๊ณตํต ์ธํฐํ์ด์ค๋ผ๊ณ ํ๋๋ฐ, ์ ๋ง ์ ๋ง ๊ฐํธํ๊ณ , Jpa์ Spring Data JPA๋ฅผ ๊ตฌ๋ถ์ง๋ ์ฒซ ๊ฑธ์์ด๋ผ ํด๋ ๋ฌด๋ฐฉํ๋ค.
public interface UserRepository extends JpaRepository<User, Long>{
}
์ด๋ฐ ํํ๋ก ์ธํฐํ์ด์ค๋ฅผ ์์!๋ง ๋ฐ์ผ๋ฉด ์ด๋ค ์ผ์ด ์๊ธธ๊น?
findByUsername, save, update ๋ฑ๊ณผ ๊ฐ์ด ๊ฐ๋จํ์ง๋ง ๋จ์ ๋ฐ๋ณต ์์ ๋ค์ Spring Data JPA ๊ตฌํ์ฒด์ธ Hibernate๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์คํ ์์ ์ ๋์ ์ผ๋ก ์์ฃผ ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ ์งํฉ์ ๋ง๋ค์ด ์ฐ๋ฆฌ์ UserRepository ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ค๋ค.
๊ทธ๋ผ ์ฐ๋ฆฌ๊ฐ ์์ฃผ ์ฌ์ฉํ๋ CRUD๋ฅผ ๊ตณ์ด JPQL๋ก ์์ฑํ์ง ์๋๋ผ๋ ์ธํฐํ์ด์ค ํ๋๋ง ์์๋ฐ์ผ๋ฉด ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
๋ค์ํ ๊ธฐ๋ฅ์ JPA ์ธํฐํ์ด์ค
JpaRepository
์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ์ผ๋ฉด ์ด๋ค ๊ธฐ๋ฅ๋ค์ ํธํ๊ฒ ์ฌ์ฉํ ์ ์์๊น?
๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ๋ค์ด ์กด์ฌํ๋ค.
findAll()
: ํด๋น ์ํฐํฐ ํ ์ด๋ธ์ ์๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ค.save()
: ๋์ ์ํฐํฐ๋ฅผ DB์ ์ ์ฅํ๋ค.saveAll()
:Iterable
๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ค.delete()
: ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋์ ์ํฐํฐ๋ฅผ ์ญ์ ํ๋ค.
์ด์ธ์๋ ๋ ๋ง์ ๊ธฐ๋ฅ๋ค์ด ์กด์ฌํ๋๋ฐ, JpaRepository
๋ ์๋์ ๊ฐ์ด ๋ค์ํ ๊ธฐ๋ฅ์ด ์ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ๊ฒ ๋๋๋ฐ, ์ด ์ธํฐํ์ด์ค๋ค ๋๋ถ์ ์ฐ๋ฆฌ๋ ๋จ์ CRUD ์ธ์ ๋ค์ํ ๊ธฐ๋ฅ(Paging, Sorting)์ ์ํํ ์ ์๋ค.
JpaRepository ์ธํฐํ์ด์ค์ ๊ณ์ธต ๊ตฌ์กฐ
JpaRepository ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
์ด์ JpaRepository๊ฐ ๋์ถฉ ๋ฌด์์ธ์ง ์์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๊ณ , ์ง๋ ์๊ฐ๊ณผ ์์์ ๋์จ Repository์ ์์์ ๋ํด์ ์ ์ ๋ด๋ณด์.
public interface UserRepository extends JpaRepository<User, Long>{
}
์ ๊ธฐ JpaRepository์ ์๋
<>
๋ ์ ๋ค๋ฆญ ์ด๋ผ๊ณ ํ๋๋ฐ, ์ ๋ค๋ฆญ์ ๋ํด์ ๋ชจ๋ฅธ๋ค๋ฉด Generic์ ํต์ฌ๋ง ์ฝ๊ฒ ์ดํดํ์๋ฅผ ํ์ธํ๊ธธ ๋ฐ๋๋ค.
์.
JpaRepository ์๋ ์ ๋ค๋ฆญ์ผ๋ก ํ์ ์ ์ง์ ํ ์ ์๋๋ฐ, ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ํฐํฐ
- ์ํฐํฐ์์ ์ฌ์ฉํ PK ๋ฐ์ดํฐ ํ์
UserRepository ๋ฅผ ์ฐ๋ฆฌ๋ User ์ํฐํฐ์ ์ค์ ๋ก ์ ๊ทผํ๋ ค๊ณ ํ๋ User
๊ณผ User ํ
์ด๋ธ, User ์ํฐํฐ์ PK ๋ฐ์ดํฐ ํ์
์ธ Long
์ ๋ฃ์ด์ค ๊ฒ์ด๋ค.
์ฌ์ฉํด๋ณด์!
์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ ์์ผ๋ก ์ฐ๋ฆฌ๊ฐ ์์ฃผ ์ฌ์ฉํ๊ฒ ๋ ์กฐํ๋ฅผ ํด๋ณด์.
User
.class- ์ํฐํฐ ํด๋์ค
UserRepository
.interface- ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ธ Repository ์ธํฐํ์ด์ค
- SpringDataJpa ์ ํ์ ๋น๋ฆฌ๊ธฐ ์ํด JpaRepository๋ฅผ ์์ ๋ฐ๋๋ค.
UserRepositoryTest
.class- ์กฐํ ์ฟผ๋ฆฌ๋ฅผ ํ ์คํธํ ํ ์คํธ ํด๋์ค
User
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
}
๊ฐ๋ ์ฑ์ ์ํด ๋กฌ๋ณต์ ์ด๋ ธํ ์ด์ ๋ค์ ์ ๊ฑฐํ๋ค.
๋กฌ๋ณต ์ด๋ ธํ ์ด์ ์ ์ฐ๋ setter/ getter, consructor๋ฅผ ๋ง๋ค๋ ๊ฐ์ธ ์ทจํฅ๊ฒ!
UserRepository
public interface UserRepository extends JpaRepository<User, Long{}
์์์ ๊ณ์ ๋งํ JpaRepository ๋ฅผ ์์ ๋ฐ์.
UserRepositoryTest
@SpringBootTest
@Transactional
@Rollback(false)
class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
void querySingleRowTest() {
// given
List<User> generatedUsers = generateUsersForTest();
userRepository.saveAll(generatedUsers);
// when
Optional<User> selectedUser = userRepository.findById(1L);
// then
assertEquals(selectedUser.get().getId(), 1L);
}
@Test
void queriesMultipleRowTest() {
// given
List<User> generatedUsers = generateUsersForTest();
userRepository.saveAll(generatedUsers);
// when
List<User> selectedAllUsers = userRepository.findAll();
// then
selectedAllUsers.forEach(selectedUser -> {
System.out.println(selectedUser.toString());
});
// then ํน์
assertAll(
() -> assertEquals(selectedAllUsers.get(0).getId(), 1L),
() -> assertEquals(selectedAllUsers.get(1).getId(), 2L),
() -> assertEquals(selectedAllUsers.get(10).getId(), 11L)
);
}
List<User> generateUsersForTest() {
List<User> users = new ArrayList<>();
for (long i = 1; i < 40; i++) {
User user = User.builder().id(i).username("์ฌ์ฉ์ " + i).build();
users.add(user);
}
return users;
}
}
์ด์ ๊ฐ์ด ํ ์คํธํ ์ ์๊ฒ ๋ค.
๋๊ธ