You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 16 Next »

JPA의 컨셉은 Java Persistence API 의 약자로 데이터 베이스에 존재하는 모델을

자바객체로 맵핑하는데 목적이 있습니다. DB는 종속적이지 않게 여러가지 DB 선택이가능하며

여기서는 단지 MYSQL을 통해 실습이 진행됩니다.



접속 DB 설정

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

spring.jpa.hibernate.ddl-auto 옵션

  • none DataBase구조 변경에 관여하지 않지않기때문에, DB 모델과 Code모델을 맞추어놓아야합니다.
  • update JPA에서 정의한 데이터모델과, 실제 데이터베이스의 모델에 변경이 있을때 반영됩니다. 

  • create 매번 데이터베이스를 생성하지만, 어플리케이션이 닫힐때 드롭하지 않습니다.

  • create-drop 매번 데이터베이스를 생성하고, 세션이 닫힐때 자동으로 데이터베이스를 드롭합니다.


Data Model(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;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

}


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


JPA에서 class를 통해 테이블을 정의할때 몇가지 규칙이 있습니다.

  • 헝가리 표기안법을 따릅니다. ( 첫글자가 대문자) 만약 Class명이 SampleTable 이라고 하면 실제 테이블명인 sample_table로 변환되게 됩니다.
  • 컬럼명은 카멜 표기법을 따릅니다. ex> sampleName 
  • 이미 존재하는 테이블및 컬럼을 참조하여 규칙이 다를시 @Table(name=”SAMPLE_TABLE”) @Column(name = “COLUMN_NAME”) 어노테이션을 통해 해결가능


CRUD 저장소생성

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

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

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


package com.psmon.springdb;

import org.springframework.data.repository.CrudRepository;

//This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
//CRUD refers Create, Read, Update, Delete

public interface UserRepository extends CrudRepository<User, Long> {

}


CRUD를 이용하여 데이터 제어하기

CURD를 이용한 데이터제어를 유닛테스트기를 이용하여 테스트해보겠습니다.

간단하게 사용자를 추가하고, 조회를 하는 코드입니다.

package com.psmon.springdb;

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.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class JparestdemoApplicationTests {
	
	@Autowired
	private UserRepository userRepository;

	@Test
	public void contextLoads() {		
		jpaTest1();		
	}
	
	public void jpaTest1() {
		// 사용자 생성
		User addUser = new User();
		addUser.setName("minsu");
		addUser.setEmail("test@x.com");
		userRepository.save(addUser);
		
		// 사용자 조회
		Iterable<User> userList = userRepository.findAll();		
		userList.forEach(item->System.out.println(item.getName() ));		
	}
}


위 코드를 SQL문으로 변환하면 아래와 같으며, 위코드는 실제 아래와같은

SQL문을 실행합니다. 

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

-- 사용자 조회
SELECT * FROM user


JPA를 활용했을때 장점은, 일괄적인 객체접근을 통해 데이터모델을 핸들링 할수가 있다란 것이며

일반적으로 소스와 통합이 되기때문에  SP이 필요없이 형상관리가 될수 있다란것이며  

데이터 모델이 고정적이지 않고 복잡한 SQL문 혹은 조인이 사용이 되는 데이터 추출에서는 권장되지 않습니다.

JPA Relation

일반적으로 DB의 테이블은 하나의 테이블에 모든 정보를 포함하지 않고, 데이터의 효율적인 관리를 위해서 몇개의 테이블구조로

나누게 됩니다.  이렇게 구조적으로 나뉜 테이블을 맵핑을 하여 하나의 테이블정보인것처럼 처리를 하려면 SQL문에서는 JOIN문으로 해결하며

JPA 객체처리모델에서는 SQL문의 JOIN문에서 해방을 하여 동일한 효과를 내려고 합니다.

그 목적을 달성하기위해 데이터베이스와 객체지향의 몇가지 차이점을 알아야합니다.

class User{
	int id;
	string name;
	string email;
}


class ClickLog{
	int clickid;
	User user;  //데이터베이스에서는 userid가 저장됨
	string clickurl;	
}



  • 프로그래밍모델에서 객체는 일반적으로 단방향 접근이 허용이 됩니다. ( ClickLog→ User )
  • 데이터 베이스는 Join문을 통해 양방향 접근이 가능합니다.( User ↔ ClickLog )
  • 프로그래밍 모델에서는 상속의 개념이 있지만, 데이터베이스 에서는 존재하지 않습니다. 


이와같은 차이를 극복하고, 데이터베이스의 Table을 객체지향적 인 모델로 변경을 하려면 JPA에서 지원하는

관계(Relation)을 형성(Join)하는 몇가지 키워드의 의미를 알아야합니다.

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


ManyToOne ( 다대일 )

위 샘플에서 사용자(User) 데이터 모델링에서,  사용자가 속한 그룹을 표한하는 테이블을 추가해보겠습니다.

여러명의 사용자는 한개의 그룹에 속할수 있기때문에, 사용자의 입장에서 다대일 입니다.

즉 사용자의 모델링에서만.., 다대일 설정을 그룹 정보와 맺어주면됩니다.


우리가 원하는 테이블 모델링은 위와같을 모습이며, user테이블에 group_id가 외래키로 설정이 되어있습니다.

어플리케이션에서 레거시한 제어를 모두한다고 하면, 이러한 외래키 설정이 필요없을수도 있으나

데이터 무결성의 입장에서 중요한 설정이며, JPA 사용시 외래키를 직접 지정하지는 않지만 

JPA에서 Relation을 사용하면, 상황에따라 외래키설정이 자동으로 됩니다. 


SQL문으로 표현했을시
CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `group_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`user_id`),
  KEY `FKa36i4ekojwk70bxen390i6tek` (`group_id`),
  CONSTRAINT `FKa36i4ekojwk70bxen390i6tek` FOREIGN KEY (`group_id`) REFERENCES `group_info` (`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;


JPA에서 정의

GroupInfo 새롭게 정의
@Entity
public class GroupInfo {
	@Id
    @GeneratedValue
	@Column(name = "GROUP_ID")
    private Integer id; 
    
    private String name;    
}

사용자 모델링에 다대일 설정
@Entity
public class User {
	@Id
    @GeneratedValue
    @Column(name = "USER_ID")
    private Integer id;

	private String name;

	private String email;
 
	@ManyToOne
	@JoinColumn(name = "GROUP_ID")
    private GroupInfo groupInfo;	
}

test
@Autowired
private GroupRepository groupRepository;


@Autowired
private UserRepository userRepository;

GroupInfo newGroup = new GroupInfo();
newGroup.setName("학생");
groupRepository.save(newGroup);

// 사용자 생성
User addUser = new User();
addUser.setName("minsu");
addUser.setEmail("test@x.com");
addUser.setGroupInfo(newGroup);        
        
userRepository.save(addUser);
 
/* 
사용자 조회, select * from user join group_info 와 동일한조회효과를 누릴수 있습니다.
전통적인 처리 방법은 Insert쿼리를 실행하고 다시조회쿼리를 실행하고,그 데이터셋의 결과를 어플리케이션에 가지고와서
사용하기 과정까지 각각 다른 처리코드와 변환과정이 필요했을것입니다.( 테이블을 쿼리로 설계 <-> SQL <-> DataSet <-> Object <-> Json )
Json의 뷰단의 데이터가 하나만 바뀌어도 최대 5가지 수정 포인트에서 코드 수정이 이루어 졌을것입니다.


JPA를 통한 데이터모델링 정의및 데이터제어가 다소 익숙하지않고 SQL문의 자유롭고 복잡한
표현을 모두 표현하기에 어려울수도 있습니다. 현재로서는 일괄적인 단일지점( 객체지향 정의)에서
모두 가능하다란것정도 이해를 해두고 넘어갑시다.
*/
Iterable<User> userList = userRepository.findAll();      
userList.forEach(item->System.out.println( String.format("Name:%s  GroupName:%s", item.getName(),item.getGroupInfo().getName() )  ));


OneToMany



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

Entity Framework

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




  • No labels