728x90

 

 

JPA란

  • JAVA Persistance API 의 줄임말로 자바 프로그램에서 관계형 데이터베이스에 접근하는 방식을 명세화한 인터페이스
  • JPA는 자바애플리케이션과 JDBC에서 작동하며 구현체는 Hibernate 라이브러리를 사용
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
EntityManager em = emf.createEntityManager();

// 엔터티 매니저를 사용하여 영속성 컨텍스트와 상호 작용
// ...

em.close();
emf.close();

애플리케이션 전체의 라이프사이클 동안 하나의 엔터티 매니저 팩토리가 생성되며,

애플리케이션 내의 여러 엔터티 매니저를 생성할 수 있음

 

 

Entity Mapping

@Entity

도메인 객체를 관계형 데이터 베이스 테이블로 맵핑할 때 사용

@Table

테이블옵션 설정가능(name, scheme, catalog)

@Access

데이터 필드 접근 / 프로퍼티 접근 방식(getter, setter) 설정가능

기본키 맵핑

@Id

기본키를 직접할당하는 경우

@GeneratedValue

기본 값을 자동생성

 

 

엔티티 설계

OneToOne

  • 일대일 관계
  • 주 테이블에 외래키 설정
@Entity
public class Person {
    @OneToOne
    private Address address;
    // ...
}

@Entity
public class Address {
    @OneToOne(mappedBy = "address")
    private Person person;
    // ...
}

OneToMany

  • 일대다 관계
  • 외래키 설정
  • 연관관계의 주인 : 외래키가 있는 테이블
  • 반대테이블은 mappedBy 속성을 사용하여 연관된 엔터티의 필드를 지정
@Entity
public class Team {
    @OneToMany(mappedBy = "team")
    private List<Member> members;
    // ...
}

@Entity
public class Member {
    @ManyToOne
    private Team team;
    // ...
}

ManyToOne

  • 다대일 관계
  • @JoinColumn 어노테이션을 사용하여 외래 키를 지정
@Entity
public class Member {
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    // ...
}

@Entity
public class Team {
    // ...
}

ManyToMany

  • 다대다 관계
  • 연관된 엔터티 간의 관계를 표현하는 연결 테이블을 자동으로 생성
  • 실무 사용 X
@Entity
public class Student {
    @ManyToMany
    @JoinTable(
        name = "STUDENT_COURSE",
        joinColumns = @JoinColumn(name = "STUDENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "COURSE_ID")
    )
    private List<Course> courses;
    // ...
}

@Entity
public class Course {
    @ManyToMany(mappedBy = "courses")
    private List<Student> students;
    // ...
}
  • 외래키 설정은 필수 아님

Cascade

부모 엔티티 변경이 자식 엔티티에 자동으로 전파되도록 함

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

 

 

엔티티 설계 - 도메인 모델 패턴

  • 엔티티가 비즈니스 로직 가지고 객체 지향의 특성을 활용 하는 방법
//==생성 메서드==//
    public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setOrderPrice(orderPrice);
        orderItem.setCount(count);

        item.removeStock(count);
        return orderItem;
    }

 

 

엔티티 설계 주의점

  1. 가급적 setter를 사용 X
  • 변경 포인트가 많아 유지보수가 어렵다
  1. 모든 연관관계는 지연로딩으로 설정 (LAZY)
  • 관련된 모든 연관관계를 가져올 수 있음
  • 특히 JPQL을 실행할 때 N + 1 문제가 자주 발생
  • 연관된 엔티티를 함께 DB에서 조회해야하면 fetch join 을 이용
  • x to one → 기본이 EAGER로 되어있음
  1. 컬렉션은 필드에서 초기화, 가급적으로 바꾸지 x

 

 

더티 체킹(dirty checking)

영속성 컨텍스트(persistence context)가 엔티티의 상태 변화를 감지하여 데이터베이스에 자동으로 변경을 반영하는 기능

 

 

Repository 설계

@PersistanceContext 어노테이션으로 EntityManater 변수를 생성

→ 스프링이 영속성 컨텍스트를 주입해 줌

→ 주입된 엔티티는 트랜잭션 커밋될 때 까지 변경되지 않음

 

 

Service 설계

@Transactional

서비스 단에서 트랜잭션을 관리하는 어노테이션 필요

사용하면, 트랜잭션을 시작하고 커밋하거나 롤백하는 코드를 작성할 필요가 없어짐

읽기전용인 경우 @Transactional(readOnly = true) 의 조건을 달아줌 → 성능 최적화됨

repostiory 필드 주입 vs 생성자 주입

필드주입

public class MemberService {
@Autowired
MemberRepository memberRepository;
...
}

생성자 주입

public class MemberService {
	private final MemberRepository memberRepository;
	public MemberService(MemberRepository memberRepository) {
	this.memberRepository = memberRepository;
}
...
}
  • 생성자 주입 방식을 권장
  • 변경 불가능한 안전한 객체 생성 가능
  • 생성자가 하나면, @Autowired 를 생략할 수 있다.
  • final 키워드를 추가하면 컴파일 시점에 memberRepository 를 설정하지 않는 오류를 체크할 수 있다.(보통 기본생성자를 추가할 때 발견)

@RequiredArgsConstructor : 파이널이 있는 필드만 생성자 주입

 

 

Controller

주의사항

api에 엔티티를 전달해서 노출하면 안됨

테스트 작성

ctrl + shift + T를 통해 테스트 클래스 만듬

JUNIT 주입

@RunWith(SpringRunner.class) @SpringBootTest @Transactional(실패시 롤백) 의 어노테이션 적용

given, when , then 방식으로 구현

Autowired로 서비스와 레파지토리 주입

assertequals 를 통한 데이터 검증

@Autowired MemberService memberService;
    @Autowired
    MemberRepository memberRepository;
    @Autowired
    EntityManager em;

    @Test
    @Rollback(false) //쿼리 찍히는 것 보기
    public void 회원가입() throws  Exception{
        //given
        Member member = new Member();
        member.setName("kim");
        //when
        Long savedId = memberService.join(member);

        //then
        em.flush(); //쿼리 나가는 것 보려면
        assertEquals(member, memberRepository.findOne(savedId));
    }
    
    
    //어노테이션으로 예외처리
    @Test(expected = IllegalStateException.class)
    public void 중복회원예외() throws  Exception{
        //given
        Member member1 = new Member();
        member1.setName("kim");

        Member member2 = new Member();
        member2.setName("kim"); //중복 상황
        //when
        memberService.join(member1);
        memberService.join(member2);

        //then
        fail("실패");
    }

 

QueryDSL

동적 쿼리, 조인 및 복잡한 쿼리를 작성할 때 사용하는 프레임 워크

 

 

변경감지와 병합

준영속 엔티티 : 영속성 컨텍스트가 관리하지 않는 객체

직접 new 를 통해 만든 객체는 JPA가 관리하지 않음

  • 변경감지(dirty checking) : em.find()
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
  • 병합 : em.merge()
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(itemParam);
}

변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이 변경된다.

병합시 값이 없으면 null 로 업데이트 할 위험도 있다. (병합은 모든 필드를 교체한다.)

변경감지를 사용하는 것이 좋다

728x90
728x90

시작이 반인데....jpa 프로젝트 설정부터 에러가 발생해버렸다 참고로 무료버전을 사용중

해결방법이 생각보다 간단해서 다행..!

 

에러메시지


Execution failed for task ':JpashopApplication.main()'. > Process 'command 'C:\Program Files\Java\jdk-17\bin\java.exe'' finished with non-zero exit value 1 * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org.

 

해결방법

 

File > Settings 메뉴 클릭 (윈도우 Ctrl + Alt + S)

  1. [Build and run using]과 [Run tests using]을 IntelliJ IDEA로 변경
  2. [Gradle JVM] 버전을 현재 프로젝트 버전과 동일한 버전으로 변경

 

 

빌드 후 정상 동작되었다

반가운 sping 모양이 나왔다 ..!!

 

728x90
728x90

 

프로젝트 중 신규 페이지와 기능을 추가하고  dto를 추가했는데,

오류 메시지가 나오면서 페이지가 전혀 뜨지 않았다......

 

에러문구

2023-06-01 16:29:30.593 ERROR 4304 --- [nio-9091-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/bus/register.html]")] with root cause org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'BusNo' cannot be found on null at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:219) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:106) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:53) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:412) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:93) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:114) ~[spring-expression-5.3.27.jar:5.3.27] at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:338) ~[spring-expression-5.3.27.jar:5.3.27] at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:265) ~[thymeleaf-spring5-3.0.15.RELEASE.jar:3.0.15.RELEASE] at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE] at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE] at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE] at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]

 

빨간 색 내용을 검색해보니,

객체가 null 이어서 조회할 수 없어 생기는 오류라고 한다.

 

간단하게 th:value 에서  dto에 ?를 붙여 해결할 수 있었다.

신규 테이블이라 아예 데이터가 없어서 생긴오류 같다.

 

적용

?만 넣어주니 페이지가 제대로 나왔다 ㅠ_ㅠ

 

 

다른 방법으로는

th:if 문으로 데이터 분기문을 사용할 수 있다고 한다.

 

아래 참고

https://ssd0908.tistory.com/entry/thymeleaf-if-else-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95

728x90
728x90

dto.java 파일을 생성 후 데이터 객체에 어노테이션을 추가했는데

빨간 줄이 뜨면서 validation 패키지 자체가 주입이 되어 있지 않는 것을 확인했다

 

Spring Validation이란?

validation이란 프로그래밍에 있어서 가장 필요한 부분이다.

Java/Kotlin 에서는 null값에 대해 접근하려고 할 때, null point exception이 발생한다. 이런 부분을 방지하기 위해 미리 검증하는 과정을 Validation이라고 함

 

 

 

검색해보니

 

spring boot 2.3 version 이상부터는 spring-boot-starter-web 의존성 내부에 있던 validation이 사라져

 spring boot version이 2.3 이상이라면 validation 의존성을 따로 추가해주어야 한다고 한다.

 

먼저 의존성을 추가해야한다

 

 

maven pom.xml 파일에 의존성 추가

아래 내용을 추가해주고 빌드하면 된다

<dependency>           
   <groupId>org.springframework.boot</groupId>           
   <artifactId>spring-boot-starter-validation</artifactId>           
   <version>2.5.2</version>       
</dependency>

 

 

 

 

validation 적용하기

dto 데이터 객체에 annotation을 추가 해주니 화면에서 잘 적용된 걸 확인할 수 있다.

 

 

이 외 가능한 유효성 검사

@Null

// null만 혀용합니다.

@NotNull

// null을 허용하지 않습니다. "", " "는 허용합니다.

@NotEmpty

// null, ""을 허용하지 않습니다. " "는 허용합니다.

@NotBlank

// null, "", " " 모두 허용하지 않습니다.

@Email

// 이메일 형식을 검사합니다. 다만 ""의 경우를 통과 시킵니다. @Email 보다 아래 나올 @Patten을 통한 정규식 검사를 더 많이 사용합니다.

@Pattern(regexp = )

// 정규식을 검사할 때 사용됩니다.

@Size(min=, max=)

// 길이를 제한할 때 사용됩니다.

@Max(value = )

// value 이하의 값을 받을 때 사용됩니다.

@Min(value = )

// value 이상의 값을 받을 때 사용됩니다.

@Positive

// 값을 양수로 제한합니다.

@PositiveOrZero

// 값을 양수와 0만 가능하도록 제한합니다.

@Negative

// 값을 음수로 제한합니다.

@NegativeOrZero

// 값을 음수와 0만 가능하도록 제한합니다.

@Future

// 현재보다 미래

@Past

// 현재보다 과거

@AssertFalse

// false 여부, null은 체크하지 않습니다.

@AssertTrue

// true 여부, null은 체크하지 않습니다.

 

 

 

728x90
728x90

spring boot mybatis 사용 시 dao, dto, service, mapper 개념에 대해 알아보자

 

DTO(Data Transfer Object)

계층간 데이터 교환을 위한 객체

DB에서 데이터를 얻어 service나 controller로 보낼 때 사용한다

로직을 갖고 있지 않은 순수한 데이터 객체이다. 데이터 타입과 사이즈를 정의할 수 있다.

@Data annotation은

@Getter / @Setter, @ToString, @EqualsAndHashCode와 @RequiredArgsConstructor 를 합친 것

 

---> 해당 프로젝트에서 게시판을 구현하기 위해 BoardDto 를 만들고 DB 테이블에 있는 데이터 객체와 동일한 객체를 생성해 주었다.

 

DAO(Data Access Object)

mapper와 dto를 연결하는 것

CRUD 기능을 정의된 메소드를 통해 구현한다

---> mapper에 id와 동일한 메소드를 정의하였다 

 

mapper

XML 파일로 쿼리를 작성하는 곳

dao에서 CRUD 함수에 해당하는 실제 쿼리를 정의해 놓은 것

#, $ 표시를 사용하여 파라미터값을 사용함

--->resultType 데이터를 반환할 형태, parameterType 파라미터의 자료형을 명시한다.

service

DAO를 여러개 묶어서 사용할 때 service를 사용

DAO로 DB에 접근하고 DTO로 데이터를 전달받은 다음, 비지니스 로직을 처리해 적절한 데이터를 반환한다.

@Autowired Repository를 통해 repository의 method를 이용 적절한 Business Logic을 처리한다.

728x90
728x90

Model만들기

dto 폴더 내 member라는 모델을 만들어주었다

 

lombok의 @Data을 이용하면 getter setter를 만들어주지 않아도 된다

 

@Data
public class Member {

  private String name;
  private String id;
  private String password;
}

 

Model Controller 연결

컨트롤러 내 생성자를 이용해 model을 가져온다

 

  @PostMapping("/login")
  public String loginChk(
    @RequestParam(name = "userId", required = true) String userId, 
    @RequestParam(name = "userPw") String userPw
  ) {
    Member pepe = new Member();

    if (userId.equals(pepe.getId()) && userPw.equals(pepe.getPassword())) {
      return ("/loginSuccess");
    } else {
      return ("/loginFail");
    }
  }
 

Model 에 데이터 담기

컨트롤러에서 바로 model에 데이터를 담을 수 있다

 addAttribute( ) 메소드를 사용한다

 
컨트롤러

import org.springframework.ui.Model;

 

@GetMapping("/") 
  public String home(Model model) { 


    System.out.println("home");
    model.addAttribute("msg", msg);
    model.addAttribute("contents", contents); //addAttribute 값 받아오기
    if (gender == 1) {
      model.addAttribute("gender", "man");
    } else if (gender == 2) {
      model.addAttribute("gender", "woman");
    }

    return "/index/index";

 

담은 데이터는 view에서 name과 value가 일치하는 곳에 뿌려줄 수 있다

 

타임리프를 사용하여  html 속성을  th:xxx 로 변경하면 모델에 addAttribute로 넣어준 데이터가 노출된다

<body>
    <h1>home</h1>
    <h2 th:text="${msg}">hi2</h2>
    <p th:text="${contents}">내용을 출력해주세요</p>
    <p th:text="${gender}">성별을 출력해주세요</p>
</body>
 
 
 
728x90
728x90

Controller란?

 - MVC에서 C에 해당 하며 주로 사용자의 요청을 처리 한 후 지정된 뷰에 모델 객체를 넘겨주는 역할을 한다.

 

 

Controller 파일 생성

1.HomeController.java 파일을 생성하고  2.Controller annotation 을 설정해준다.

-Controller 폴더 내에 HomeController를 만들어주었다.

-annotation 설정

@Controller
public class HomeController {
}
 
 

 

GetMapping API 만들기

 @GetMapping으로 annotation 설정해준다
> Get 방식이란? : querystring으로 데이터 통신
> Post 방식이란? : body로 데이터 통신
 
  @GetMapping("/") //라우팅 설정 하는 부분 라우팅 = 경로설정
  public String login() {
    return "/login"; //login html 페이지로 return해주겠다는 의미
  }
 
@GetMapping("/home"
  public String home() {
    return "/home";
  }
 
login.html과 home.html는 templates 폴더 내 만들어주었다
 
 

@Requestparam으로 파라미터 값 받기

@GetMapping("/")
  public String login(
    @RequestParam String userId,
    @RequestParam String userPw) {
    return "/login";
  }

param의 변수명과 view페이지에서 데이터를 받을 속성의 name 값이 일치해야한다

 

API 결과 확인해보기

728x90
728x90
maven 설정파일 다운로드

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf/2.7.10

 

소스 파일 - > pom.xml 파일에서 depebdecies 내에 가져오기

html 파일에서 링크 연결
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head>

 

타임리프 표현식
th:[속성]=”${출력 값 또는 조건식}”

ex )  <p th:text ="${loginMsg}">로그인되었습니다</p>
728x90
728x90

visual studio code로 진행하는 spring boot : gradle 프로젝트 환경설정

 

프로젝트 생성

ctrl shift p 누른 후 커맨드 팔레트(Command palette)를 열어

>gradle 누르고 엔터 >

 

Dependnecies 선택

 

spring boot dashboard 확장팩 설치

Apps > 에서 디버깅이 가능

 

* localhost 설정에 문제가 있는 경우 별도로 port 설정을 해준다

application.properties 에서 포트 설정

server.port = 8080

 

728x90

+ Recent posts