Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Tip

JPA의 컨셉은 Java Persistence API 의 약자로 두가지 큰 목적을 가지고 있습니다.있으며

  • 데이터 베이스에 존재하는 모델링을 자바객체로 맵핑을 시킴
  • SP/SQL방식을 최대한 억제하고, OOP중심적 Entity설계및 영속성 Repository를 통한 데이터 제어

JPA와 별개로 데이터베이스를 잘 활용하는 방법은 데이터베이스를 잘 이해하는것임으로

연관부분이 있을시 JPA의 특성과 같이 설명 진행 예정입니다.


설명을 위해, 축약된 코드로 진행되며, 풀소스는 아래에서 확인가능합니다위 특징은, 스프링의 웹요소와 연동할때도 유용하게 쓰입니다.


CodeLink : http://git.webnori.com/projects/WEBF/repos/spring_jpa/browseJPA 개념 추가설명

Expand
title목차펼치기

Table of Contents


접속 DB 설정

...


No Format
# application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/spring
spring.datasource.username=test
spring.datasource.password=test1234

...

Code Block
languagejava
themeEmacs
titleUser Entity
package com.psmon.springdb;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity // This tells Hibernate to make a table out of this class
public class User {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer id;
	
	private String name;
	
	private String email;


    @Column(columnDefinition="char(1)")
    private String categorycode1;

}


실제 DataBase에서는 위와같은 테이블이, 어플리케이션 시작시 자동 생성됩니다.

CRUD 저장소생성


DB 종속 특수한 필드는(ex> char(1),  columnDefinition을 통핸 맵핑처리가능합니다.)

DateTime의 경우 좀더 다양한 맵핑전략이 필요하며 다음 링크를 참고합니다:

http://www.developerscrappad.com/228/java/java-ee/ejb3-jpa-dealing-with-date-time-and-timestamp/



CRUD 저장소생성

...


 전통적인 DataBase를 이용하는  전통적인 DataBase를 통한 개발방법은,  SQL을 직접이용하거나, 주로 관리되는 SP를 이용하여 Table의 정보를 읽거나 변경을 하였습니다.

JPA에서는 CrudRepository를 이용하여 조금더 객체 지향접근방식을 통해 Database를 제어할수가 있습니다제어하려고 합니다.

User테이블을 제어하는 객체를 정의하기 위해서, 여기서는 UserRepository 라고 정의를 하였습니다.

...

Code Block
languagesql
themeEmacs
-- 사용자생성
INSERT INTO `spring`.`user`
(`id`,
`email`,
`name`)
VALUES
(<{id:}>,
<{email: >,
<{name:}>);

-- 사용자 조회
SELECT * FROM user



JPA

...

RelationShip

...


일반적으로 DB의 테이블은 하나의 테이블에 모든 정보를 포함하여 설계하지않고,

데이터의 효율적인 관리를 위해서 몇개의 테이블구조로 나누어 저장후 외래키로 연관관계  연관관계 형성을하게 됩니다. 

이렇게 구조적으로 나뉜 테이블을 하나의 테이블정보인것처럼 정보를 머지를 하려면 JOIN을 통해 테이블을 지배해야합니다.

JPA 객체처리모델에서는 SQL문의 JOIN문을 직접적으로 사용하지 않고, 동일한 효과를 객체포함/상속등 OOP의 특성으로

데이터베이스의 관계 형성과 유사한 효과를 내려고 합니다. 그 목적을 달성하기위해 데이터베이스의 테이블과 객체지향의 클래스의 몇가지 차이점을 알아야합니다.

Class VS Table

...

Class

...

Table

...

class User{
	int id;   // 특별한 구현없이, 사용자의 객체만으로 자신을 포함하는 특정정보(클릭로그)만 추출할수가 없습니다.
	string name;
	string email;
}
class ClickLog{
	int clickid;
	User user; // User객체 참조를 포함합니다.
	string clickurl;	
}

TABLE User{
   int id   //자신의 키를 참조하는 다른 테이블에 접근가능합니다. on u.id = c.clickid
   varchar name
   varchar email   
}
TABLE ClickLog{
	int clickid
	int userid  //외래키를 통해서 접근가능합니다. on c.clickid = u.id
	varchar clickurl;
}

Class GroupInfo{

string name;

}


Class User{

GroupInfo groupInfo;

string name;

string email;

}


Class GroupInfoNew{

string name;

List<User> userList;

}

GroupInfo

Image Added


User

Image Added



select * from user u join groupinfo g on g.group_id=u.user_id


  • Class : 포함되는 객체는, 자신을 포함하는 객체 찾기가 어려우며,주인객체로 단방향접근이 일반적입니다. ( User → GroupInfo )
  • Table : 테이블은  자유로운 결합이 가능하며 연관관계 아이디를 통해 양방향접근이 가능합니다.  ( User ↔ GroupInfo )
  • Class : 클래스는 객체자체를 리스트화하여 가질수 있습니다.
  • Table : 테이블은 리스트형태의 데이터자체를 포함하는것은 불가능하며, 일대다 관계가 형성된 테이블을 통해 논리적 구성을 하여야합니다
  • 프로그래밍모델에서 객체는 일반적으로 상속또는 포함관계를 통해 단방향 접근이 일반적입니다.. ( ClickLog→ User )
  • 데이터 베이스는 연관관계와는 별개로,  양방향 접근에 자유롭습니다. ( User ↔ ClickLog )
  • 프로그래밍 모델에서는 상속의 개념이 존재하여 다양한 객체를 포함할수가있습니다. 데이터베이스 에서는 상속개념이 없으며 리스트와같은 데이터Type을 포함할수 없습니다.
  • 데이터베이스에서는 상속의 개념을, 외래키를 통해 관계형성을 하며 논리적인 관계를 형성할수가 있습니다.

이와같은 차이를 극복하고, 데이터베이스의 Table을 객체지향적 인 모델로 변경을하여 사용하려면

JPA에서 지원하는 관계Class(Relation)을 형성(Join)하는 OOP)에 데이터베이스의 관계도(Relation)를 형성하는 몇가지 키워드의 의미를 알아야합니다.

아래 키워드는 OOP가 가진 특성을 잃지않고  OOP가 가진 한계를 관계형 DB처럼 , 관계형 DB의 속성을 확장을 해줍니다.

  • ManyToOne : 다대일 관계 매칭정보
  • JoinColumn : 외래키를 매핑때 사용함
  • mappedBy : 연관관계설정시 주인이 아님을 설정
  • OneToMany : 일대다 관계 매핑정보
  • OneToOne : 일대일 관계 매칭정보, 어느곳이나 외래키를 가짐
  • ManyToMany : 다대다 관계매칭정보, 맵핑테이블을 만들어서 사용하기를 권장
  • 연관관계주인 : 외래키가 있는곳이 주인이며 주인만이 수정가능 아닌경우 조회만가능


외래키/인덱스

JPA를 사용하면 , 테이블생성을 SQL문으로 직접 하지 않는 전략을 선택하기 때문에테이블 설계를 OOP를 통해 Entity(Class)에 직접 하는 전략을 선택합니다.

그리고 그것은 자동적으로 테이블을 만들어내는 DDL기능까지 수행할수 있습니다.

자신이 설계한, Entity 인덱스설정/외래키설정등이  자동적으로 생성됩니다. 자신이 설계한, JPA CLASS가 어떠한 테이블 스키마에서

어떠한 관계가 형성이되고 인덱스를 설정하는지 파악하는것은 중요한 사안입니다.


Warning

어플리케이션에서 테이블 생성전략(DDL)은 일반적으로 개발단계까지 허용이되며,

운영에서 어플리케이션이 테이블 구조를 변경하는것은 허용하지 않는 정책을 사용합니다.

DDL기능을 OFF시키거나, 어플리케이션 내에는 변경 코드 자체가 없을수도 있습니다.

어플리케이션 개발자에게 DBA에 준하는 권한을 줘야 개발/운영/배포가 가능한 구조에서

어플리케이션 에게 DDL을 무조건 금지하는 정책이 어떠한 의미를 가지는지 고민이 필요해보입니다

반대로, 테이블 생성을 개발자가 할수 없는 체계라고 하면, spring.jpa.hibernate.ddl-auto=none 전략으로 갈수도 있습니다.

이경우 운영 DB의 테이블 업데이트는 어플리케이션이 할수 없는 권한구조가 미리 약속되었다라고 해둡시다.

JPA에서는 어쨋든 기존 테이블 스키마를 ,OOP로 옮기려고 할것이고 테이블에 설계된 외래키 설정및 1:? 관계를 파악하지 못하고

JPA를 사용하려고 한다면,  연관된 테이블의 참조를 얻기가 불가능할것입니다.

SQL중심적 개발에서는 자유롭게 JOIN을 하여 모든 테이블 참조를 얻을수도 있고 집합도 만든반면,

JPA에서는 테이블의 연관성을 파악하지 못하면 OOP 레벨에서 아무것도 할수 없는 상황이 생깁니다.

이러한 제약은 오히려 데이터베이스를 더 공부해야하는 요소라고 볼수 있습니다.


식별관계 VS 비식별관계

  • 식별 관계는 부모 테이블의 기본 키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 점점 늘어난다. 그러면 조인할 때 SQL이 복잡해지고 기본 키 인덱스가 불필요하게 커질 수 있다.
  • 식별 관계는 2개 이상의 컬럼을 합해서 복합 기본 키를 만들어야 하는 경우가 많다.
  • 식별 관계를 사용할 때 기본 키로 비지니스 의미가 있는 자연 키 컬럼을 조합하는 경우가 많다. 반면에 비식별관계의 기본 키는 비지니스와 전혀 관계없는 대리 키를 주로 사용한다. 
  • 언제든지 요구사항은 변한다. 식별 관계의 자연 키 컬럼들이 자식에 손자까지 전파되면 변경하기 힘들다.
  • 식별 관계는 부모 테이블의 기본 키를 자식 테이블의 기본 키로 사용하므로 비식별 관계보다 테이블 구조가 유연하지 못하다.

어느 한가지가 항상 유리한것은 아니기때문에, 키 전파가 되는것에 따라 이러한 차이가 있다라고 알우둡니다.


Info


이후 샘플은 비식별관계만 이용하여, JPA 연관특성을 살펴 보겠습니다.


...

Code Block
languagesql
themeEmacs
-- 그룹을 추가한다.
INSERT INTO test.group_info(
   group_id
  ,name
) VALUES (
   NULL -- group_id - IN int(11)
  ,''  -- name - IN varchar(255)
)


-- 사용자를 추가할시 그룹을 지정하거나, 나중에 지정한다. group id가 현재는 null 허용
INSERT INTO test.user(
   user_id
  ,email
  ,name
  ,group_id
) VALUES (
   NULL -- user_id - IN int(11)
  ,''  -- email - IN varchar(255)
  ,''  -- name - IN varchar(255)
  ,0   -- group_id - IN int(11)
)


-- 여러명을 등록할시 위 과정이 반복됩니다.



--학생인 사용자만 조회시..Join을 통해 해결
select * from user left outter join group_info gi on gi.name='학생'

...

Code Block
languagejava
themeEmacs
		//Test를 위해 100개의 데이터 인입
		GroupInfo newGroupA = new GroupInfo("학생");
		Set usersA = new HashSet<User>() {{
			for(int i=0; i<100 ; i++) {
				String userName = String.format("minsu%d", i);
				String email = String.format("min%d@x.com", i);
				add(new User(userName,email,newGroupA));
			}					
		}};
		newGroupA.setUsers(usersA);
        groupRepository.save(new HashSet<GroupInfo>() {{
        	add(newGroupA);
        }});


		//원하는 페이지를 조회합니다.( 페이지번호 , 페이지당 처리수)
        PageRequest  pageRequest =  new PageRequest(1,10);                       
        Page<User> sPage = userPageRepo.findAll(pageRequest);
        System.out.println( String.format(" %d:Contents %d:Page", sPage.getNumberOfElements(),sPage.getNumber()      ) );
        
        Page<User> sPagesPage2 = userPageRepo.findAllfindByGroupInfoName("학생", pageRequest);
        System.out.println( String.format(" %d:Contents %d:Page", sPagesPage2.getNumberOfElements(),sPagesPage2.getNumber() ) );
        
        Page<User> sPage2 = userPageRepo.findByGroupInfoName("학생", pageRequest);
        System.out.println( String.format(" %d:Contents %d:Page", sPage2.getNumberOfElements(),sPage2.getNumber() 


페이지와 소팅 동시처리
No Format
pageRequest =  new PageRequest(0,10, Sort.Direction.DESC , "a","b","c"  );
pageRequest =  new PageRequest(0,10, Sort.Direction.DESC , "a",Sort.Direction.ASC,"b"  );
다중 소팅
No Format
Sort.Direction sortDir = Sort.Direction.DESC;
if( sortdir.equals("asc") ) sortDir = Sort.Direction.ASC;

Sort sortOpt = new Sort( sortDir, sort )
						.and( new Sort(Sort.Direction.DESC, "customervaluation") );

JPQL MODE

Code Block
languagejava
themeEmacs
public interface UserPageRepo extends Repository<User, Long>{

	// QueryMode
	@Query(value="select t from User t "			
			+ "where  t.name =:name  "
			+ "order by t.id " , nativeQuery=false )
	List<User> findBySomeName( @Param("name") String name, Pageable pageable);
	
}

...

nativeQuery , true/flase에따라 작동방식이 변경가능하며 , 특수한 경우가 아니면

JPQL사용이 권장됩니다.


다른 유용한 사이트:

JPA 개념 설명 : http://blog.woniper.net/255




Info

다른 진영(.net) 에서도 JPA와 유사하게 데이터를 제어하고 유사한 스펙을 가지고 있습니다.

Entity Framework : https://docs.microsoft.com/en-us/ef/core/

NHibernate : https://en.wikipedia.org/wiki/NHibernate /


http://www.sqler.com/401779

이 방식을 언급하는것은 누가먼저하고? 어느것이 좋으냐?

차이점을 비교하는것이아닙니다.

서로에게 영향을 받았고, 해외의경우 성공케이스와 더불어

데이터베이스와 관련된 어플리케이개발이 이러한 방식으로


서로의 어플리케이션진영에서 데이터베이스를 어떻게 다룰것인가?

에대한 고민이 비슷해 보이며 서로에게 영향을 주면서 계속 발전중인듯보입니다.

마치 웹에서 MVC패턴 적용이 진영간 표준화 된것처럼

데이터베이스를 다루는 기술도 표준화를 이끌어내기를 기대해봅니다.


진영간 유사한 스펙에 다른 기술적 차이가고 있다라는것입니다.


JAVA(JPA).NET(EntityFrame)
모델정의Entity-클래스기본클래스클래스
저장소정의Persitent 저장소명RepositoryDbContext
쿼리툴QueryDSLLinq

안타까운 사실은 , SQL/SP중심적 개발이 국내를 포함하여

아시아에서는 압도적입니다.( 구글 트렌드 : JPA vs MyBatis )

Image Removed