{"id":24591989,"url":"https://github.com/venh312/jpashop","last_synced_at":"2025-03-18T02:44:30.734Z","repository":{"id":226609974,"uuid":"769118013","full_name":"venh312/jpashop","owner":"venh312","description":"💻 ORM 표준 JPA 프로그래밍 (회원, 상품, 주문)","archived":false,"fork":false,"pushed_at":"2024-06-06T12:41:04.000Z","size":105,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-24T10:13:18.728Z","etag":null,"topics":["java17","jpa","springboot3"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/venh312.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-08T11:34:19.000Z","updated_at":"2024-06-06T12:41:08.000Z","dependencies_parsed_at":"2024-03-22T11:52:02.684Z","dependency_job_id":"4342ddeb-ff35-41be-bbb3-92b7cfddbce1","html_url":"https://github.com/venh312/jpashop","commit_stats":null,"previous_names":["conf312/jpashop","venh312/jpashop"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/venh312%2Fjpashop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/venh312%2Fjpashop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/venh312%2Fjpashop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/venh312%2Fjpashop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/venh312","download_url":"https://codeload.github.com/venh312/jpashop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244147216,"owners_count":20405940,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java17","jpa","springboot3"],"created_at":"2025-01-24T10:13:22.820Z","updated_at":"2025-03-18T02:44:30.715Z","avatar_url":"https://github.com/venh312.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 영속성 컨텍스트\n![image](https://github.com/conf312/jpashop/assets/13326651/1a55cb27-ea35-46d3-b9c8-2a319639ab33)\n\n### 비영속\n영속성 컨텍스트와 전혀 관계가 없는 새로운 상태\n```java\nMember member = new Member(\"kim\");\n```\n\n### 영속\n영속성 컨텍스트에 관리되는 상태\n```java\nMember member = new Member(\"kim\");\n\nEntityMangager em = entityManagerFactory.createEntityManager();\nem.persist(member); // 이제 영속성 컨텍스트에서 관리한다.\n```\nem.persist(member); 를 한다고 DB에 저장되는 것은 아니다. `커밋` 혹은 `flush()` 이후에 DB에 저장된다. 그전까지는 영속성 컨텍스트에만 존재.\n\n### 준영속\n\n영속성 컨텍스트에 저장되었다가 분리된 상태\n```java\nMember member = new Member(\"kim\");\n\nEntityMangager em = entityManagerFactory.createEntityManager();\nem.detach(member);\n```\n개발자가 직접 준영속을 만들 일은 거의 없음\n\n## 트랜잭션\n- em.persist() 트랜잭션이 커밋되면서 영속성 컨텍스트가 플러시 된다. 이때 트랜잭션과 영속성 컨텍스트가 종료된다.\n- em.findById() 저장한 엔티티를 조회하면 새로운 영속성 컨텍스트가 생성된다.\n\n\n\n## 연관 관계 참조 및 조인\n실무 JPA에서는 @OneToMany, @ManyToMany 조인 사용을 지양한다. (@ManyToOne 도 되도록이면 사용하지 않는다고 한다.)\n\nDB에 관계를 맺는 순간 규모가 커질 수록 수정에 제약이 따르고, 양방향 연관관계에서는 무한루프 등, 특히 JPA에서는 영속성 전이나 고아객체에 대한 지식이 부족하면 미궁에 빠지기 쉽상이기 때문이다.\n\n하지만, 알고 사용하지 않는거와 사용해보지 않고 사용하지 말라하여 사용하지 않는것은 천지차이 이기 때문에 이 프로젝트에서는 다양한 연관 관계 조인 방식을 사용해보며 학습한다.\n\n## 엔티티 그래프\n엔티티 조회시점에 연관된 엔티티들을 함께 조회하는 기능이다.\n\n### 1. Named 엔티티 그래프\n### @NamedEntityGraph로 정의한다.\n```java\n@NamedEntityGraph(name = \"Order.withMember\", attributeNodes = {@NamedAttributeNode(\"member\"})\n@Entity\npublic class Order {\n...\n}\n```\n### 엔티티 그래프 사용\n```java\nEntityGraph graph = em.getEntityGraph(\"Order.withMember\");\nMap hints = new HashMap();\nhints.put(\"javax.persistence.fetchgraph\", graph);\n\nOrder order = em.find(Order.class, orderId, hinst);\n```\n\n### 2. JPQL에서 엔티티 그래프\n```java\nList\u003cOrder\u003e resultList =\n  em.createQuery(\"select o from Order o where o.id = :orderId\", Order.class\n  .setParameter(\"orderId\", orderId)\n  .setHint(\"javax.persistence.fetchgraph\", e.getEntityGraph(\"Order.withAll\"))\n  .getResultList();\n```\n\n## 어노테이션\n### @Transactional(readonly = true)\n데이터를 변경하지 않는 트랜잭션에서 플러시를 생략하여 약간의 성능 향상을 얻을 수 있다.\n\n### @DiscriminatorColumn \nItem 클래스의 하위 클래스를 식별하는 데 사용될 컬럼을 지정하고, `@DiscriminatorValue`는 각 하위 클래스가 해당 컬럼에 저장될 값을 명시합니다.\n\n따라서 데이터베이스 테이블에서 `DTYPE` 컬럼에 'A'값을 가진 레코드는 Album 인스턴스에 해당하고, 'B'값을 가진 레코드는 Book의 인스턴스에 해당합니다.\n\n## N+1\n지연로딩을 사용하되, JPQL 페치 조인을 사용하자.\n\n- @OneToOne, @ManyToOne: 기본 페치 전략은 즉시로딩\n- @OneToMany, @ManyToMany: 기본 페치 전략은 지연로딩\n\n따라서 기본값이 즉시 로딩인 @OnetoOne, @ManyToOne은 `fetch = FetchType.LAZY`로 설정해서 지연 로딩 전략을 사용하도록 하자.\n\n## 읽기 전용 쿼리 성능 최적화\n엔티티가 영속성 컨텍스트에 관리되면 1차 캐시부터 변경 감지까지 얻을 수 있는 혜택이 더 많다. 하지만 영속성 컨텍스트는 변경감지를 위해 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용하는 단점이 있다. 예를 들어 100건의 구매 내용을 출력하는 단순환 조회가 있다고 가정해보자. 그리고 조회한 엔티티를 다시 조회할 일도 없고 수정할 일도 없이 딱 한번만 화면에 출력하면 된다. 이때는 읽기 전용으로 엔티티를 조회하면 메모리 사용량을 최적화 할 수 있다.\n\n### @Transactional(readOnlny = true)\n스프링 프레임워크가 하이버네이트 세션의 플러시 모드를 MANUAL로 설정한다. 이렇게 하면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않는다. 따라서 트랜잭션을 커밋해도 영속성 컨텍스트를 플러시 하지않는다. 영속성 컨텍스트를 플러시 하지 않으니 등록, 수정, 삭제는 당연히 동작하지 않는다. 하지만 플러시할 때 일어나는 스냅샷 비교와 같은 무거운 로직들을 수행하지 않으므로 성능이 향상된다. 트랜잭션 과정은 이루어지되 단지 영속성 컨텍스트를 플러시하지 않을 뿐이다.\n\n\n## @PersistenceContext 란?\n- EntityManager를 빈으로 주입할 때 사용하는 어노테이션\n- 스프링에서는 영속성 관리를 위해 EntityManager가 존재\n- 스프링 컨테이너가 시작될 때 EntityManager를 만들어서 빈으로 등록\n- 이 때 스프링이 만들어둔 EntityManager를 주입받을 때 사용\n- @PersistenceContext로 지정된 프로퍼티에 아래 두 가지 중 한 가지로 EntityManager를 주입\n- EntityManagerFactory에서 새로운 EntityManager를 생성하거나 Transaction에 의해 기존에 생성된 EntityManager를 반환\n\n### @PersistenceContext를 사용해야 하는 이유\n#### EntityManager를 사용할 때 주의해야 할 점\n여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하여 쓰레드 간에는 EntityManager를 공유해서는 안됩니다. 하지만 스프링은 싱글톤 기반으로 동작하기에 빈은 모든 쓰레드가 공유됩니다.\n\n**그러나 @PersistenceContext으로 EntityManager를 주입받아도 동시성 문제가 발생하지 않는 이유는**\n- 스프링 컨테이너가 초기화되면서 @PersistenceContext으로 주입받은 EntityManager를 Proxy로 감쌉니다.\n- 그리고 EntityManager 호출 시 마다 Proxy를 통해 EntityManager를 생성하여 Thread-Safe를 보장합니다.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvenh312%2Fjpashop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvenh312%2Fjpashop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvenh312%2Fjpashop/lists"}