Spring Boot 학습 노트 06 -- JPA

요약
본문 을 보고 나 면 다음 과 같은 지식 을 습득 할 것 이다.
  • Spring Boot 프로젝트 의 JPA 설정 및 사용 방법
  • Spring Boot 프로젝트 에 Spring Data JPA 를 설정 하 는 방법
  • Spring Data JPA 와 Atomikos 를 통합 하여 다 중 데이터 소스 사무 관리 실현
  • JPA 확장 방법
  • SpringBoot 시리즈: SpringBoot 학습 노트
    머리말
    JPA 는 Java Persistence API 로 O / R 맵 을 기반 으로 하 는 표준 규범 으로 이 규범 은 규칙 의 표준 (주해 또는 인터페이스) 만 정의 하고 구체 적 인 실현 을 제공 할 필요 가 없 으 며 구체 적 인 실현 은 소프트웨어 제공 업 체 에 의 해 이 루어 집 니 다. 현재 주요 한 JPA 공급 업 체 는 Hibernate, EclipseLink 와 OperJPA 입 니 다.
    Spring Data JPA 는 Spring Data 의 하위 프로젝트 로 JPA 기반 Repository 를 제공 함으로써 코드 양 을 간소화 합 니 다.org. spring from work. data. jpa. reposcory. Jpa Repository 를 제공 합 니 다. 저희 Repository 는 이 Jpa Repository 를 계승 하면 JPA 가 가 져 온 좋 은 점 을 누 릴 수 있 습 니 다.
    Spring Boot 는 spring - boot - starter - data - jpa 를 통 해 JPA 에 대한 지원 을 제공 합 니 다. Spring Boot 의 기본 JPA 실현 자 는 Hibernate 입 니 다.
    아래 의 내용 을 설명 하기 전에, 우 리 는 먼저 데이터베이스 에 표를 한 장 만 들 었 다.
    #    1
    CREATE SCHEMA `springboot1` DEFAULT CHARACTER SET utf8 ;
    CREATE TABLE `springboot1`.`person` (
      `p_id` INT NOT NULL AUTO_INCREMENT COMMENT '  ',
      `p_name` VARCHAR(45) NULL COMMENT '  ',
      `p_age` INT NULL COMMENT '  ',
      PRIMARY KEY (`p_id`))
    ENGINE = InnoDB
    COMMENT = '     ';
    
    INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('1', '  ', '20');
    INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('2', '  ', '25');
    INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('3', '  ', '18');
    INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('4', '  ', '18');
    

    Spring Boot 항목 에 JPA 사용 하기
    프로젝트 를 만 들 때 JPA 의존 도 를 선택 하거나 spring - boot - starter - data - jpa 를 pom 에 수 동 으로 추가 합 니 다.
    
        org.springframework.boot
        spring-boot-starter-data-jpa
    
    

    이 항목 은 다음 두 개의 자동 설정 클래스 를 자동 으로 엽 니 다.
    JpaRepositoriesAutoConfiguration HibernateJpaAutoConfiguration
    application. properties 에 jpa 관련 설정 추가
    #datasource
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8
    spring.datasource.username=root
    spring.datasource.password=newpwd
    
    #spring_jpa
    #              ,       ,     ,      ;validate:        ,       ;
    spring.jpa.hibernate.ddl-auto=update
    #     sql
    spring.jpa.show-sql=true
    #       json     
    spring.jackson.serialization.indent-output=true
    

    프로젝트 에서 JPA 를 사용 할 때 JpaRepository 에 계승 되 는 Repository 인 터 페 이 스 를 만 들 면 JpaRepository 와 부모 클래스 에서 제공 하 는 모든 데이터 접근 방법 을 가 질 수 있 습 니 다.만약 제공 하 는 방법 이 업무 수 요 를 만족 시 키 지 못 한다 면 다음 과 같은 규칙 에 따라 데이터 방법 을 확장 할 수 있다.
    JpaRepository
    package org.springframework.data.jpa.repository;
    
    import java.io.Serializable;
    import java.util.List;
    import org.springframework.data.domain.Example;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.repository.NoRepositoryBean;
    import org.springframework.data.repository.PagingAndSortingRepository;
    import org.springframework.data.repository.query.QueryByExampleExecutor;
    
    @NoRepositoryBean
    public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor {
        List findAll();
    
        List findAll(Sort var1);
    
        List findAll(Iterable var1);
    
         List save(Iterable var1);
    
        void flush();
    
         S saveAndFlush(S var1);
    
        void deleteInBatch(Iterable var1);
    
        void deleteAllInBatch();
    
        T getOne(ID var1);
    
         List findAll(Example var1);
    
         List findAll(Example var1, Sort var2);
    }
    

    Repository: PersonRepository 를 사용자 정의 하고 데이터 접근 방법 을 확장 합 니 다. 구체 적 인 확장 방법 은 예제 코드 를 참조 하 십시오.
    package com.example.dao;
    
    import com.example.model.Person;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    import java.util.List;
    
    public interface PersonRepository extends JpaRepository {
    
        //1.                ,              ,                 。
        //        ,         
    
        //  
        List findByPName(String PName);
    
        //And ---     SQL    and    ;  
        List findByPNameAndPAge(String PName, Integer PAge);
    
        // Or ---     SQL    or    ;  
        List findByPNameOrPAge(String PName, Integer PAge);
    
        //Between ---     SQL    between    ;  
        List findByPAgeBetween(Integer min, Integer max);
    
        //LessThan ---     SQL    " findByPAgeLessThan(Integer max);
    
        //LessThanEqual ---     SQL    "<=";
        List findByPAgeLessThanEqual(Integer max);
    
        //GreaterThan ---     SQL   ">";         After   
        List findByPAgeGreaterThan(Integer min);
    
        //GreaterThanEqual ---     SQL   ">=";
        List findByPAgeGreaterThanEqual(Integer min);
    
        //IsNull ---     SQL    "is null";
        List findByPNameIsNull();
    
        //IsNotNull ---     SQL    "is not null";
        List findByPNameIsNotNull();
    
        //NotNull ---   IsNotNull   ;  
        List findByPNameNotNull();
    
        //Like ---     SQL    "like";
        List findByPNameLike(String PName);
    
        //NotLike ---     SQL    "not like";
        List findByPNameNotLike(String PName);
    
        //OrderBy ---     SQL    "order by";
        List findByPNameNotNullOrderByPAgeAsc();
    
        //Not ---     SQL    "! =";
        List findByPNameNot(String PName);
    
        //In ---     SQL    "in";
        List findByPNameIn(String PName);
    
        //NotIn ---     SQL    "not in";
        List findByPNameNotIn(String PName);
    
    
        //Top ---             ,   First   
        List findTop2ByPName(String PName);
    
        //2.      @Query  ,        ,          ,       @Modifying  
        //      
        @Query("select p from Person p where p.pName = :name and p.pAge = :age")
        List withNameAndAgeQuery(@Param("name") String name, @Param("age") Integer age);
    
        //      
        @Query("select p from Person p where p.pName = ?1 and p.pAge = ?2")
        List withNameAndAgeQuery2(String name, Integer age);
    
    
        //    ,  hql,     sql,    nativeQuery = true
        @Query(value = "delete from Person where pId=?1")
        @Modifying
        int deletePersonById(Integer id);
    
        //    
        @Query(value = "update Person set pName=?1 where pId=?2 ")
        @Modifying
        int updatePersonName(String name, Integer id);
    
        //    ,  sql  
        @Query(value = "insert into person(p_name,p_age) value(?1,?2)",nativeQuery = true)
        @Modifying
        int insertPersonByParam(String name, Integer age);
    
    
        //3.            ,         Pageable pageable    ,     Page  
        Page findByPNameNot(String name, Pageable pageable);
    
        //      
        @Query("select p from Person p where p.pName = :name ")
        Page withNameQueryPage(@Param("name") String name, Pageable pageable);
    }
    

    POJO 실체 대상: Person
    package com.example.model;
    import javax.persistence.*;
    
    import static javax.persistence.GenerationType.IDENTITY;
    
    @Entity
    @Table(name = "person"
            , catalog = "springboot1"
    )
    public class Person implements java.io.Serializable {
    
        @Id
        @GeneratedValue(strategy = IDENTITY)
        @Column(name = "p_id", unique = true, nullable = false)
        private Integer pId;
    
        @Column(name = "p_name", length = 45)
        private String pName;
    
        @Column(name = "p_age")
        private Integer pAge;
    
        //setter and getter
    
        @Override
        public String toString() {
            return "Person{" +
                    "pId=" + pId +
                    ", pName='" + pName + '\'' +
                    ", pAge=" + pAge +
                    '}';
        }
    }
    

    테스트 데모
    package com.example;
    
    import com.example.dao.PersonRepository;
    import com.example.model.Person;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Iterator;
    import java.util.List;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Transactional
    public class JpaSingleDatasourceApplicationTests {
    
        @Autowired
        private PersonRepository personRepository;
    
        @Test
        public void findByPName() {
            String name = "  ";
            List list = personRepository.findByPName(name);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
        @Test
        public void findByPNameAndPAge() {
            String name = "  ";
            int age = 18;
            List list = personRepository.findByPNameAndPAge(name,age);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
        @Test
        public void findByPNameOrPAge() {
            String name = "  ";
            int age = 25;
            List list = personRepository.findByPNameOrPAge(name,age);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
        @Test
        public void findTop2ByPName() {
            String name = "  ";
            List list = personRepository.findTop2ByPName(name);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
        @Test
        public void withNameAndAgeQuery() {
            String name = "  ";
            int age = 18;
            List list = personRepository.withNameAndAgeQuery(name,age);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
        @Test
        public void withNameAndAgeQuery2() {
            String name = "  ";
            int age = 18;
            List list = personRepository.withNameAndAgeQuery2(name,age);
            System.out.println(list.size());
            for(Person person : list){
                System.out.println(person);
            }
        }
    
    
        @Test
        public void deletePersonById(){
            int id = 1;
            int result = personRepository.deletePersonById(id);
            System.out.println("result = " + result);
        }
    
        @Test
        public void updatePersonName(){
            int id = 1;
            String name = "  ";
            int result = personRepository.updatePersonName(name,id);
            System.out.println("result = " + result);
        }
    
        @Test
        public void insertPersonByParam(){
            int age = 10;
            String name = "  ";
            int result = personRepository.insertPersonByParam(name,age);
            System.out.println("result = " + result);
        }
    
        @Test
        public void findByPNameNot(){
            String name = "  ";
            //  
            Sort sort = new Sort(Sort.Direction.DESC, "pId");
            //     ,       
            Pageable pageable = new PageRequest(0, 3, sort);
    
            Page pages = personRepository.findByPNameNot(name,pageable);
            System.out.println("pages.getTotalElements()" + pages.getTotalElements());
            System.out.println("pages.getTotalPages()" + pages.getTotalPages());
            Iterator it=pages.iterator();
            while(it.hasNext()){
                System.out.println("value:"+((Person)it.next()));
            }
        }
    
        @Test
        public void withNameQueryPage(){
            String name = "  ";
            //  
            Sort sort = new Sort(Sort.Direction.DESC, "pId");
            //     ,       
            Pageable pageable = new PageRequest(1, 3, sort);
    
            Page pages = personRepository.withNameQueryPage(name,pageable);
            System.out.println("pages.getTotalElements()" + pages.getTotalElements());
            System.out.println("pages.getTotalPages()" + pages.getTotalPages());
            Iterator it=pages.iterator();
            while(it.hasNext()){
                System.out.println("value:"+((Person)it.next()));
            }
        }
    }
    

    Spring Boot 프로젝트 에서 Spring Data JPA 를 설정 하 는 방법
    spring - boot - starter - data - jpa 에 의존 하지 않 으 려 면 설정 류 를 통 해 Spring Boot 가 Spring Data JPA 에 대한 지원 을 실현 할 수 있 습 니 다.
    pom 교체 의존 은 여기 서 설명 합 니 다. 실제로 spring - boot - starter - data - jpa 의 의존 을 교체 하지 않 아 도 됩 니 다. 교체 의 장점 은 필요 하지 않 은 jar 에 대한 의존 을 줄 이 는 것 입 니 다.
        
            mysql
            mysql-connector-java
            5.1.37
        
    
        
            javax.transaction
            jta
            1.1
        
    
        
            org.aspectj
            aspectjweaver
            1.8.9
        
    
        
        
            org.hibernate
            hibernate-core
            4.3.5.Final
        
    
        
            org.hibernate
            hibernate-entitymanager
            4.3.5.Final
        
    
        
            org.springframework
            spring-orm
            4.3.3.RELEASE
        
    
        
            org.springframework.data
            spring-data-jpa
            1.10.5.RELEASE
        
    
        
    

    사용자 정의 설정 클래스: DataSourceConfig
    package com.example;
    
    import org.hibernate.jpa.HibernatePersistenceProvider;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.Database;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    
    @Configuration
    @EnableTransactionManagement(proxyTargetClass = true)
    //  Spring Data JPA   
    @EnableJpaRepositories(basePackages = "com.example.dao", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
    public class DataSourceConfig {
    
        @Value("${spring.datasource.driver-class-name}")
        String driverClass;
        @Value("${spring.datasource.url}")
        String url;
        @Value("${spring.datasource.username}")
        String userName;
        @Value("${spring.datasource.password}")
        String passWord;
    
        @Bean(name = "dataSource")
        public DataSource dataSource() {
    
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driverClass);
            dataSource.setUrl(url);
            dataSource.setUsername(userName);
            dataSource.setPassword(passWord);
            return dataSource;
        }
    
        //  jpa     
        @Bean(name = "transactionManager")
        public PlatformTransactionManager transactionManager() {
            JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
            jpaTransactionManager.setDataSource(dataSource());
            jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
            return jpaTransactionManager;
        }
    
        @Bean
        public JpaVendorAdapter jpaVendorAdapter() {
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setShowSql(true);
            adapter.setDatabase(Database.MYSQL);
            adapter.setGenerateDdl(true);
            return adapter;
        }
    
    
        @Bean(name = "entityManagerFactory")
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
            entityManager.setDataSource(dataSource());
            entityManager.setJpaVendorAdapter(jpaVendorAdapter());
            entityManager.setPackagesToScan("com.example.model");// entity package
            entityManager.setPersistenceProviderClass(HibernatePersistenceProvider.class);
            return entityManager;
        }
    }
    

    프로젝트 시작 클래스 에서 jpa 의 자동 설정 을 닫 습 니 다:
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,JpaRepositoriesAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
    public class JpaSingleDatasourceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(JpaSingleDatasourceApplication.class, args);
        }
    }
    

    Spring Data JPA 와 Atomikos 를 통합 하여 다 중 데이터 소스 사무 관 리 를 실현 합 니 다.
    spring - data - jpa 는 기본적으로 Hibernate 를 사용한다 고 하지만 Atomikos 통합 방식 과 Hibernate 는 약간 다르다.
    pom
    
                com.atomikos
                transactions-jdbc
                4.0.4
            
            
                com.atomikos
                transactions-jta
                4.0.4
            
            
                com.atomikos
                transactions
                4.0.4
            
            
                com.atomikos
                atomikos-util
                4.0.4
            
    
            
                javax.transaction
                jta
                1.1
            
    
            
                org.aspectj
                aspectjweaver
                1.8.9
            
    
            
            
                org.hibernate
                hibernate-core
                4.3.5.Final
            
    
            
                org.springframework
                spring-orm
                4.3.3.RELEASE
            
    
            
                org.springframework.data
                spring-data-jpa
                1.10.5.RELEASE
            
    
            
                org.hibernate
                hibernate-entitymanager
                4.3.5.Final
            
    
            
                mysql
                mysql-connector-java
                5.1.37
            
    
            
                org.springframework.boot
                spring-boot-starter-web
            
    
            
                org.springframework.boot
                spring-boot-starter-test
                test
            
    

    application.properties
    #datasource
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8
    spring.datasource.username=root
    spring.datasource.password=newpwd
    
    #datasource2
    spring.datasource.driver-class-name2=com.mysql.jdbc.Driver
    spring.datasource.url2=jdbc:mysql://localhost:3306/springboot2?useUnicode=true&characterEncoding=utf-8
    spring.datasource.username2=root
    spring.datasource.password2=newpwd
    

    MainConfig: Atomikos 사무 관리자 등록 에 사용
    package com.example;
    
    import com.atomikos.icatch.jta.UserTransactionImp;
    import com.atomikos.icatch.jta.UserTransactionManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.jta.JtaTransactionManager;
    
    import javax.transaction.TransactionManager;
    import javax.transaction.UserTransaction;
    
    @Configuration
    public class MainConfig {
    
        @Bean(name = "userTransaction")
        public UserTransaction userTransaction() throws Throwable {
            UserTransactionImp userTransactionImp = new UserTransactionImp();
            userTransactionImp.setTransactionTimeout(10000);
            return userTransactionImp;
        }
    
        @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
        public TransactionManager atomikosTransactionManager() throws Throwable {
            UserTransactionManager userTransactionManager = new UserTransactionManager();
            userTransactionManager.setForceShutdown(true);
            return userTransactionManager;
        }
    
        @Bean(name = "transactionManager")
        @DependsOn({ "userTransaction", "atomikosTransactionManager" })
        public PlatformTransactionManager transactionManager() throws Throwable {
            UserTransaction userTransaction = userTransaction();
            TransactionManager atomikosTransactionManager = atomikosTransactionManager();
            JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, atomikosTransactionManager);
            jtaTransactionManager.setAllowCustomIsolationLevels(true);
            return jtaTransactionManager;
        }
    
        //       ,       bean
        @Bean(name = "atomikosJtaPlatfom")
        public AtomikosJtaPlatfom atomikosJtaPlatfom(){
            AtomikosJtaPlatfom atomikosJtaPlatfom = new AtomikosJtaPlatfom();
            try {
                atomikosJtaPlatfom.setTm(atomikosTransactionManager());
                atomikosJtaPlatfom.setUt(userTransaction());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
    
            return atomikosJtaPlatfom;
    
        }
    }
    

    JPA 의 LocalContainer Entity Manager Factory Bean 을 설정 할 때 JTA 를 지원 할 수 있 도록 하려 면 Jpa Properties 를 설정 할 때 다음 과 같은 인 자 를 지정 해 야 합 니 다.
    hibernate.transaction.jta.platform hibernate.current_session_context_class hibernate.transaction.factory_class
    다음 에 LocalContainer Entity Manager Factory Bean 을 설정 할 때 해당 하 는 설정 을 볼 수 있 습 니 다. 여기 서 말 하고 자 하 는 것 은 hibernate.transaction.jta.platform 지정 org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform 의 실현 류 가 필요 합 니 다. 그 주요 기능 은 javax. transaction. TransactionManager 와 javax. transaction. UserTransaction 을 연결 하 는 것 입 니 다.
    spring - data - jpa 는 이러한 실현 클래스 를 제공 하지 않 았 으 나, hibenate 는 많은 실현 클래스 를 제공 하 였 으 며, spring boot 도 하나의 실현 클래스 인 SpringJ taPlatform 을 제공 하 였 으 나, 이러한 실현 클래스 는 모두 구조 함수 로 자바 x. transaction. Manager 와 자바 x. transaction. UserTransaction 을 연결 하 였 으 며, 결 성 된 구조 방법 을 제공 하지 않 았 기 때문에 속성 지정 hibernate.transaction.jta.platform 을 통 해 이 루어 졌 다.스프링 은 이 실현 클래스 를 초기 화 할 수 없습니다.
    따라서 구현 클래스 를 만 들 고 set 방법 으로 javax. transaction. TransactionManager 와 javax. transaction. UserTransaction 을 연결 할 수 있 습 니 다.이게 바로 Atomikos JtaPlatfom 입 니 다.
    package com.example;
    
    import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
    
    import javax.transaction.TransactionManager;
    import javax.transaction.UserTransaction;
    
    public class AtomikosJtaPlatfom extends AbstractJtaPlatform {
    
        private static UserTransaction ut;
        private static TransactionManager tm;
        @Override
        protected TransactionManager locateTransactionManager() {
            return tm;
        }
    
        @Override
        protected UserTransaction locateUserTransaction() {
            return ut;
        }
    
        public UserTransaction getUt() {
            return ut;
        }
    
        public void setUt(UserTransaction ut) {
            AtomikosJtaPlatfom.ut = ut;
        }
    
        public TransactionManager getTm() {
            return tm;
        }
    
        public void setTm(TransactionManager tm) {
            AtomikosJtaPlatfom.tm = tm;
        }
    }
    
    

    다음은 설정 클래스 에 LocalContainer Entity Manager Factory Bean 을 등록 해 야 합 니 다. @ EnableJpa Repositories 주 해 는 같은 설정 클래스 에 두 번 설명 할 수 없 기 때문에 데이터 원본 에 따라 각각 설정 합 니 다.
    JpaConfigDs 1: 데이터 원본 1
    package com.example;
    
    import com.atomikos.jdbc.AtomikosDataSourceBean;
    import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.Database;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement(proxyTargetClass = true)
    //     1 Repository  ,   1 entityManagerFactory,       
    @EnableJpaRepositoryies(basePackages = "com.example.dao.ds1", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
    public class JpaConfigDs1 {
    
        @Value("${spring.datasource.driver-class-name}")
        String driverClass;
        @Value("${spring.datasource.url}")
        String url;
        @Value("${spring.datasource.username}")
        String userName;
        @Value("${spring.datasource.password}")
        String passWord;
    
    
        @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close")
        public DataSource dataSource() {
            System.out.println("dataSource init");
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            mysqlXaDataSource.setUrl(url);
            mysqlXaDataSource.setPassword(passWord);
            mysqlXaDataSource.setUser(userName);
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    
            AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mysqlXaDataSource);
            xaDataSource.setUniqueResourceName("dataSource");
            xaDataSource.setMinPoolSize(10);
            xaDataSource.setPoolSize(10);
            xaDataSource.setMaxPoolSize(30);
            xaDataSource.setBorrowConnectionTimeout(60);
            xaDataSource.setReapTimeout(20);
            xaDataSource.setMaxIdleTime(60);
            xaDataSource.setMaintenanceInterval(60);
    
            return xaDataSource;
        }
    
        @Bean(name = "jpaVendorAdapter")
        public JpaVendorAdapter jpaVendorAdapter() {
            System.out.println("jpaVendorAdapter init");
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setShowSql(true);
            adapter.setDatabase(Database.MYSQL);
            adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
            adapter.setGenerateDdl(true);
            return adapter;
        }
    
        @Bean(name = "entityManagerFactory")
        @DependsOn({"atomikosJtaPlatfom"}) //     atomikosJtaPlatfom
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            System.out.println("entityManagerFactory init");
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
    
            entityManager.setJpaVendorAdapter(jpaVendorAdapter());
            // entity package
            entityManager.setPackagesToScan("com.example.model.ds1");
            entityManager.setJtaDataSource(dataSource());
    
            Properties properties = new Properties();       
            properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
            properties.put("hibernate.show_sql", "true");
            properties.put("hibernate.format_sql", "true");
    
            //jta  
            properties.put("hibernate.current_session_context_class", "jta");
            properties.put("hibernate.transaction.factory_class", "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory");
            //           AtomikosJtaPlatfom
            properties.put("hibernate.transaction.jta.platform","com.example.AtomikosJtaPlatfom");
            entityManager.setJpaProperties(properties);
            return entityManager;
    
        }
    }
    

    JpaconfigDs 2: 데이터 원본 2
    package com.example;
    
    import com.atomikos.jdbc.AtomikosDataSourceBean;
    import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.Database;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement(proxyTargetClass = true)
    @EnableJpaRepositories(basePackages = "com.example.dao.ds2", entityManagerFactoryRef = "entityManagerFactory2", transactionManagerRef = "transactionManager")
    public class JpaConfigDs2 {
    
        @Value("${spring.datasource.driver-class-name2}")
        String driverClass;
        @Value("${spring.datasource.url2}")
        String url;
        @Value("${spring.datasource.username2}")
        String userName;
        @Value("${spring.datasource.password2}")
        String passWord;
    
    
        @Bean(name = "dataSource2", initMethod = "init", destroyMethod = "close")
        public DataSource dataSource() {
            System.out.println("dataSource2 init");
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            mysqlXaDataSource.setUrl(url);
            mysqlXaDataSource.setPassword(passWord);
            mysqlXaDataSource.setUser(userName);
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    
            AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mysqlXaDataSource);
            xaDataSource.setUniqueResourceName("dataSource2");
            xaDataSource.setMinPoolSize(10);
            xaDataSource.setPoolSize(10);
            xaDataSource.setMaxPoolSize(30);
            xaDataSource.setBorrowConnectionTimeout(60);
            xaDataSource.setReapTimeout(20);
            xaDataSource.setMaxIdleTime(60);
            xaDataSource.setMaintenanceInterval(60);
    
            return xaDataSource;
        }
    
        @Bean(name = "jpaVendorAdapter2")
        public JpaVendorAdapter jpaVendorAdapter() {
            System.out.println("jpaVendorAdapter2 init");
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setShowSql(true);
            adapter.setDatabase(Database.MYSQL);
            adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
            adapter.setGenerateDdl(true);
            return adapter;
        }
    
        @Bean(name = "entityManagerFactory2")
        @DependsOn({"atomikosJtaPlatfom"})
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            System.out.println("entityManagerFactory2 init");
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
    
    
            entityManager.setJpaVendorAdapter(jpaVendorAdapter());
            entityManager.setPackagesToScan("com.example.model.ds2");// entity package
            entityManager.setJtaDataSource(dataSource());
    
            Properties properties = new Properties();
            properties.put("hibernate.transaction.jta.platform","com.example.AtomikosJtaPlatfom");
    
            properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
            properties.put("hibernate.show_sql", "true");
            properties.put("hibernate.format_sql", "true");
            properties.put("hibernate.current_session_context_class", "jta");
            properties.put("hibernate.transaction.factory_class", "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory");
    
            entityManager.setJpaProperties(properties);
            return entityManager;
    
        }
    }
    

    다른 면 에 서 는 단일 데이터 원본 이 JPA 를 사용 하 는 것 과 다 르 지 않 으 며, 여 기 는 코드 를 나열 하지 않 습 니 다.
    JPA 확장 방법
    일반적인 상황 에서 우리 의 Repository 인 터 페 이 스 는 JpaRepository 를 계승 하기 때문에 JpaRepository 가 제공 하 는 모든 방법 을 기본 으로 사용 할 수 있 습 니 다. 만약 에 제공 하 는 방법 이 수 요 를 만족 시 키 지 못 할 경우 자신의 Repository 에서 명명 규칙 이나 @ Query 주석 등 을 통 해 방법 을 확장 할 수 있 습 니 다.그렇다면 우리 가 모든 Repository 가 이 확장 기능 을 가 질 수 있 도록 공공 을 확장 하 는 방법 을 부모 클래스 에 두 고 싶다 면 어떻게 실현 해 야 합 니까?
    이 예 는 예 를 들 어 조회 조건 의 페이지 별 조 회 를 받 고 조회 할 때 실체 대상 을 전달 하 는 속성 에 따라 처리 하 며 문자열 이 라면 모호 하 게 일치 하지 않 으 면 정확하게 일치 합 니 다.
    부모 인터페이스 정의 -- BaseJpaRepository
    @NoRepositoryBean //              Repository
    public interface BaseJpaRepository extends JpaRepository,JpaSpecificationExecutor {
    
        Page findByAuto(T example, Pageable pageable);
    }
    

    구현 클래스 만 들 기 -- BaseJpaRepository Impl
    public class BaseJpaRepositoryImpl extends SimpleJpaRepository implements BaseJpaRepository {
    
        //         EntityManager
        private final EntityManager entityManager;
        public BaseJpaRepositoryImpl(Class domainClass, EntityManager entityManager) {
            super(domainClass, entityManager);
            this.entityManager = entityManager;
        }
    
        //      ,             BaseSpecs
        @Override
        public Page findByAuto(T example, Pageable pageable) {
            return findAll(BaseSpecs.byAuto(entityManager,example),pageable);
        }
    }
    

    BaseSpecs 의 by Auto 방법 은 검색 대상 Specification 을 패키지 하고 실체 대상 을 전달 하 는 속성 에 따라 처리 합 니 다. 문자열 이 라면 모호 하 게 일치 하지 않 으 면 정확하게 일치 합 니 다.
    public class BaseSpecs {
    
        public static  Specification byAuto(final EntityManager entityManager, final T example){
            final Class type = (Class) example.getClass();
            return new Specification() {
                @Override
                public Predicate toPredicate(Root root, CriteriaQuery> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
                    List predicateList = new ArrayList<>();
                    EntityType entityType = entityManager.getMetamodel().entity(type);
    
                    for(Attribute attribute : entityType.getDeclaredAttributes()){
                        Object attrValue = getValue(example,attribute);
                        if(attrValue != null){
                            if(attribute.getJavaType() == String.class){
                                if(!StringUtils.isEmpty(attrValue)){
                                    predicateList.add(criteriaBuilder.like(root.get(attribute(entityType,attribute.getName(),String.class)),pattern((String)attrValue)));
                                }
                            }else{
                                predicateList.add(criteriaBuilder.equal(root.get(attribute(entityType,attribute.getName(),attrValue.getClass())),attrValue));
                            }
                        }
                    }
                    return predicateList.isEmpty()?criteriaBuilder.conjunction():criteriaBuilder.and(toArray(predicateList));
                }
    
                private  Object getValue(T example,Attribute attr){
                    return ReflectionUtils.getField((Field)attr.getJavaMember(),example);
                }
    
                private  SingularAttribute attribute(EntityType entityType,String fieldName,Class fieldClass){
                    return entityType.getDeclaredSingularAttribute(fieldName,fieldClass);
                }
    
                private Predicate[] toArray(List predicateList){
                    Predicate[] array = predicateList.toArray(new Predicate[predicateList.size()]);
                    return array;
                }
            };
        }
    
        static private String pattern(String str){
            return "%" + str + "%";
        }
    }
    

    우리 의 Repository 가 Jpa Repository 를 실현 할 때 Spring - data - jpa 는 우리 에 게 Jpa Repository 의 실현 류 Simple Jpa Repository 를 동적 으로 사용 하 는 이 유 를 설명 합 니 다. 이것 은 우리 가 인 터 페 이 스 를 만 들 고 실현 류 를 제공 하지 않 아 도 되 는 이유 입 니 다.
    여기 서 우 리 는 새로운 부모 인터페이스 BaseJpa Repository 를 만 들 었 고 실현 류 BaseJpa Repository Impl 을 제공 했다. 그래서 우 리 는 Spring - data - jpa 에 게 우리 자신의 실현 류 를 사용 해 야 하 며 Simple Jpa Repository 를 사용 해 서 는 안 된다 고 알려 야 한다. 그래서 우 리 는 Jpa Repository Factory Bean 을 바 꿔 야 한다.
    Jpa Repository Factory Bean 에 계승 할 BaseRepository Factory Bean 을 만 듭 니 다:
    public class BaseRepositoryFactoryBean, S, ID extends Serializable>   extends JpaRepositoryFactoryBean {
    
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)    {
            return new BaseRepositoryFactory(entityManager);
        }
    }
    
    class BaseRepositoryFactory extends JpaRepositoryFactory {
        public BaseRepositoryFactory(EntityManager entityManager){
            super(entityManager);
        }
    
    
        //     
        @Override
        protected  SimpleJpaRepository, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
            BaseJpaRepositoryImpl customRepository = new BaseJpaRepositoryImpl((Class)information.getDomainType(),entityManager);
            return customRepository;
    
        }
    
        //       
        @Override
        protected Class> getRepositoryBaseClass(RepositoryMetadata metadata)     
            return BaseJpaRepositoryImpl.class;
        }
    }
    

    또한 @ EnableJpa Repositories 주석 에서 지정 합 니 다:
    @EnableJpaRepositories(basePackages = "com.example.dao", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager",repositoryFactoryBeanClass=BaseRepositoryFactoryBean.class)
    public class JpaConfig {
        //………………
    }
    

    사용자 정의 저장 소 계승 BaseJpaRepository
    public interface PersonRepository extends BaseJpaRepository {
        //………                ………
    
    }
    

    테스트
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Transactional
    public class JpaExtendApplicationTests {
    
        @Autowired
        private PersonRepository personRepository;
    
        @Test
        public void findByAuto() {
            Person person = new Person();
            person.setpName("  ");
            person.setpAge(18);
            Sort sort = new Sort(Sort.Direction.DESC, "pId");
            //     ,       
            Pageable pageable = new PageRequest(0, 3, sort);
            Page list = personRepository.findByAuto(person,pageable);
            for(Person p:list){
                System.out.println(p);
            }
        }
    }
    

    본문 예제 코드 다운로드 주소:https://github.com/hanqunfeng/SpringBootStudy

    좋은 웹페이지 즐겨찾기