site 데이터베이스에서 테이블을 다 삭제한다.
그리고 user 테이블을 만들고 컬럼을 만들어 save를 해준다.
persist를 눌려준다.
오류가 뜬다..
기본키 설정 안했기 때문에 오류가 났다.
idx컬럼명에서 마우스 우측을 눌려 new constraint를 해준다.
constraints에 들어가면 기본키가 설정된 것을 볼 수 있다.
post테이블을 만들어 위와 똑같이 해준다. 다하고 properties에 들어가 면 아래의 사진과 같아야 한다.
user테이블에서 row추가를 해서 데이터를 넣고 save해준다.
post도 마찬가지로 데이터 넣고 save해준다.
post테이블에서 외래키를 설정해준다.
post의 useridx랑 user의 idx연결해준다.
fk를 가지고 있는 건 post고 참조하는게 user다
save함
서버를 만들기 위해 spring initializr을 만든다.
프로젝트를 만들 때는 com.~.~ 이렇게 된다.
패키지명은 거꾸로 만든다.
프로젝트 세팅에 대해 적혀있다.
dependencies {
// 이게 젤 중요
// 원하는 라이브러리 입력만 적어도 줘도 알아서 세팅해줌
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
주석적고 나서 프로젝트 다시 로드해야 한다.
원래는 다 다운로드해서 세팅해줘야 하는데 스프링 부트를 사용하면 아래의 이런 것들을 다 신경쓰지 않아도 됨.
실행해보면 url오류 난다
properties를 shift + f6을 눌러 yml로 변경한다.
connection에 들어가 url을 클릭해 주소를 가져온다.
jdbc:mariadb://localhost:3306/ -> 데이터베이스 시스템
데이터베이스 site 적어줘야함
yml안에서 이게 젤 중요하다.
spring:
datasource:
url: jdbc:mariadb://localhost:3306/site
username: root
password: 1234
나머지 yml코드들이다.
jpa:
defer-datasource-initialization: true
open-in-view: false
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
ddl-auto: none # create-drop, update, validate, none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
show-sql: true
servlet:
multipart:
max-request-size: 10MB
max-file-size: 10MB
나중에 실무가서도 그냥 복붙만 하면된다.
코드에서 모르는 거는 gpt한테 물어보면 된다.
실행하면 start가 뜨고 성공했다.
localhost로 들어가면
오류가 뜬다..
해결하기 위해 컨트롤러를 만들어 준다.
package com.example.site2.domain.main.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MainController {
public ModelAndView mainPage(){
ModelAndView modelAndView = new ModelAndView();
return modelAndView;
}
}
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org/">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
실행하면 화면만 나온다.
유저 리스트를 출력하기 위해 body안에 만들어준다.
<body>
<h1>유저 리스트</h1>
<ul>
<li>
<div>유저idx : <span>1</span></div>
<div>유저id : <span>홍길동</span></div>
</li>
</li>
<li>
<div>유저idx : <span>2</span></div>
<div>유저id : <span>임꺽정</span></div>
</li>
</ul>
</body>
controller도 다시 작성한다.
@Controller
public class MainController {
@GetMapping("/")
public ModelAndView mainPage(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("main");
return modelAndView;
}
}
다시 실행하면 임시의 유저 리스트가 나온다.
확장자는 컴퓨터가 아니라 우리가 구분하기 위해 쓰는것
즉, html이지만 html 파일이 아니라는 거
modelAndView보다 String을 많이 한다고 해서 변경해준다.
@Controller
public class MainController {
@GetMapping("/")
public String mainPage(){
return "main";
//화면의 이름이 문자열 일테니까
//modelandview가 리턴에 있다.
}
// @GetMapping("/")
// public ModelAndView mainPage(){
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.setViewName("main");
// return modelAndView;
// }
}
구조를 이렇게 만듦
주석으로 설명
package com.example.site2.model.user.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity //결국 테이블
@Table(name = "`user`") // 연계한다는 말
//user테이블은 예약어라 ``사용
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 오라클은 시퀀스 나머지는 identity
@Column(name = "idx", nullable = false, unique = true) //기본키이기 때문에 unique해줘야함, 유일한 값이니까
private Long idx; //idx는 int로 되어있는데 long으로 해줘야함
@Column(name = "id", nullable = false)
private String id;
@Column(name = "password", nullable = false)
private String password;
// 데이터베이스에서는 띄어쓰기는 _, java에서는 대문자
}
package com.example.site2.model.post.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "post")
public class PostEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx", nullable = false, unique = true)
private Long idx;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "content", nullable = false)
private String content;
@Column(name = "user_idx", nullable = false)
private Long userIdx;
}
참고자료
기본키 매핑 어노테이션 정리
@AllArgsConstructor //생성자 자동 만들어줌
@NoArgsConstructor //기본 생성자
@Getter
UserEntity, PostEntity 클래스 위에 붙여준다.
주석으로 설명
package com.example.site2.model.user.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.site2.model.user.entity.UserEntity;
// 데이터를 가져오는 역할
// interface로 해줌
public interface UserRepository extends JpaRepository<UserEntity, Long> { //user의 idx가 long타입이기 때문에
}
package com.example.site2.model.post.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.site2.model.post.entity.PostEntity;
public interface PostRepository extends JpaRepository<PostEntity, Long> { // 엔티티랑, 기본키 타입
}
id는 중복이 되면 안되니까 db에 들어가 id컬럼에 unique키 설정을 해준다.
그러고 save를 해준다.
UserEntity에 id를 바꿔준다.
@Column(name = "id", nullable = false, unique = true)
private String id;
엔티티를 가져오기 위해 repository를 만들어줌
package com.example.site2.model.user.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.site2.model.user.entity.UserEntity;
// 데이터를 가져오는 역할
// interface로 해줌
public interface UserRepository extends JpaRepository<UserEntity, Long> { //user의 idx가 long타입이기 때문에
// select * from user where idx = ?
// interface로 하면 함수형식으로 지가 클래스를 만듦
Optional<UserEntity> findByIdx(Long idx);
// 데이터가 없으면 null이 된다
// UserEntity가 null인걸 표현하는 객체이다
// null체크를 하기 위함
// select * from user where idx = ?
Optional<UserEntity> findById(String id); // id로 찾으면 1개 또는 0이니까 optional로 쓴다.
// select * from user where password = ?
List<UserEntity> findByPassword(String password);
// select *
// from user
// where idx = ?
// and id = ?
Optional<UserEntity> findByIdxAndId(Long idx, String id);
// 값이 하나만 나옴, idx랑 id는 중복이 안되니까
// select *
// form user
// where id like '%?%';
List<UserEntity> findByIdContaining(String id);
}
// 원래라면 이렇게 해야하지만 귀찮기 때문에 jpa를 사용한다
// class UserRepo{
// Optional<UserEntity> findByIdx(Long idx){
// Connection ~~
// PreparedStatement ~~
// ResultSet ~~
// }
// }
주석을 참고
package com.example.site2.model.post.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.example.site2.model.post.entity.PostEntity;
import java.util.List;
public interface PostRepository extends JpaRepository<PostEntity, Long> { // 엔티티랑, 기본키 타입
// 쿼리문으로 데이터 가져오기 (복잡한 쿼리 쓰고 싶으면)
@Query(value = "select * from post where title = ?", nativeQuery = true) // sql문을 그대로 작성하면 됨
void getPostList(String title);
// jpa는 복잡한 조인이 안될 것 같이 생김 그래서 entity로 넘어가본다.
// select * from post where idx = ?
Optional<PostEntity> findByIdx(Long idx);
// select * from post where title =?
List<PostEntity> findByTitle(String title);
// select * form post where content = ?
List<PostEntity> findByContent(String content);
// select * from post wher user_idx = ?
List<PostEntity> findByUserIdx(Long userIdx);
// select *
// from post
// where title = ?
// or content = ?
List<PostEntity> findByTitleOrContent(String title, String content);
// select *
// from post
// where title like '%?%'
// or content like '%?%';
List<PostEntity> findByTitleContainingOrContentContaining(String title, String content);
}
주석을 참고
// user_idx랑 연관되는 애를 reference에 넣어줌
// 이렇게만 작성해주면 알아서 join이 되서 다 가져와줌
// join문 안적어도 됨
@JoinColumn(name = "user_idx", referencedColumnName = "idx")
private UserEntity userEntity;
엔티티들 클래스 위에 붙여준다.
@ToString
repository를 가져와서 만든다.
package com.example.site2.domain.main.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.site2.model.user.entity.UserEntity;
import com.example.site2.model.user.repository.UserRepository;
@Service
@Transactional(readOnly = true)
public class MainService {
@Autowired
private UserRepository userRepository;
public void getMainData(){
List<UserEntity> userEntityList = userRepository.findAll();
Optional<UserEntity> userEntity = userRepository.findByIdx(1L); // Long타입이니까 L붙여줌
System.out.println(userEntity);
}
}
mainService를 @Autowired해준다.
public class MainController {
@Autowired
private MainService mainService;
@GetMapping("/")
public String mainPage(){
mainService.getMainData();
return "main";
//화면의 이름이 문자열 일테니까
//modelandview가 리턴에 있다.
}
}
실행하면 오류가 뜸
PostRepository에서 findByUserIdx를 삭제해준다.
// select * from post wher user_idx = ?
List<PostEntity> findByUserIdx(Long userIdx);
그리고 폴인키라 다음과 같이 변경해준다.
@ManyToOne
@JoinColumn(name = "user_idx", referencedColumnName = "idx", nullable = false)
private UserEntity userEntity;
다시 실행하면 start가 나오고 성공이다
optional을 null처리해준다.
@Service
@Transactional(readOnly = true)
public class MainService {
@Autowired
private UserRepository userRepository;
@Autowired
private PostRepository postRepository;
public void getMainData(){
Optional<PostEntity> postEntityOptional = postRepository.findByIdx(1L);
if(postEntityOptional.isEmpty()){ //null이면
System.out.println("엔티티가 없습니다.");
}
// 있다면
// 애가 진짜 entity
PostEntity postEntity = postEntityOptional.get();
System.out.println(postEntity);
}
}
실행해서 localhost들어가서 새로고침 하면

user의 정보가 들어있고 join을 쓰지않고 jpa를 사용하면 가져올 수 있다.
근데 이렇게 하면 안되고 가공해서 던져줘야한다.
@Service
@Transactional(readOnly = true)
public class MainService {
@Autowired
private UserRepository userRepository;
// @Autowired
// private PostRepository postRepository;
public void getMainData(){
// 이렇게 하면 안됨
// 가공을 해서 던져줘야함
List<UserEntity> userEntityList = userRepository.findAll();
// Optional<PostEntity> postEntityOptional = postRepository.findByIdx(1L);
// if(postEntityOptional.isEmpty()){ //null이면
// System.out.println("엔티티가 없습니다.");
// }
// // 있다면
// // 애가 진짜 entity
// PostEntity postEntity = postEntityOptional.get();
// System.out.println(postEntity);
}
}
화면에 필요한 것만 가져오도록 DTO를 만들어 준다.
package com.example.site2.domain.main.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
// data transfer object = dto
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class MainUserDTO {
// 화면에서 필요한 것만
private Long idx;
private String id;
}
다시 만들어준다.
(주석을 참고)
@Service
@Transactional(readOnly = true)
public class MainService {
@Autowired
private UserRepository userRepository;
// @Autowired
// private PostRepository postRepository;
public void getMainData(){
// 이렇게 하면 안됨
// 가공을 해서 던져줘야함
// 원두 가져오기
List<UserEntity> userEntityList = userRepository.findAll();
// 아메리카노 컵 준비하기
List<MainUserDTO> mainUserDTOList = new ArrayList<>();
for (UserEntity userEntity : userEntityList) {
// 원두를 아메리카노로 만들기
MainUserDTO mainUserDTO = new MainUserDTO(userEntity.getIdx(), userEntity.getId());
// 아메리카노를 컵에 담기
// list에 담는다.
mainUserDTOList.add(mainUserDTO);
}
}
}
DTO를 담을 DTO를 만들어준다.
package com.example.site2.domain.main.dto;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class ResMainDTO {
// DTO들 담는 캐리어 같은 친구
private List<MainUserDTO> mainUserDTOList;
}
주석을 참고
@Service
@Transactional(readOnly = true)
public class MainService {
@Autowired
private UserRepository userRepository;
// @Autowired
// private PostRepository postRepository;
public ResMainDTO getMainData(){
// 이렇게 하면 안됨
// 가공을 해서 던져줘야함
// 원두 가져오기
List<UserEntity> userEntityList = userRepository.findAll();
// 아메리카노 컵 준비하기
List<MainUserDTO> mainUserDTOList = new ArrayList<>();
for (UserEntity userEntity : userEntityList) {
// 원두를 아메리카노로 만들기
MainUserDTO mainUserDTO = new MainUserDTO(userEntity.getIdx(), userEntity.getId());
// 아메리카노를 컵에 담기
// list에 담는다.
mainUserDTOList.add(mainUserDTO);
}
// 캐리어에 아메리카노들 담고 컨트롤러한테 넘겨주기
return new ResMainDTO(mainUserDTOList);
}
}
controller를 수정해준다.
package com.example.site2.domain.main.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.site2.domain.main.dto.ResMainDTO;
import com.example.site2.domain.main.service.MainService;
@Controller
public class MainController {
@Autowired
private MainService mainService;
// 두 개 다 같다! 편한걸로 쓰면 됨
@GetMapping("/")
public String mainPage1(Model model){
ResMainDTO dto = mainService.getMainData();
model.addAttribute("dto", dto);
return "main";
//화면의 이름이 문자열 일테니까
//modelandview가 리턴에 있다.
}
// @GetMapping("/")
// public ModelAndView mainPage(){
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.addObject("dto", dto);
// modelAndView.setViewName("main");
// return modelAndView;
// }
}
thymeleaf를 사용한다.
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org/">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>유저 리스트</h1>
<ul>
<li th:each="user : ${dto.mainUserDTOList}">
<div>유저idx : <span th:text="${user.idx}"></span></div>
<div>유저id : <span th:text="${user.id}"></span></div>
</li>
</ul>
</body>
</html>
정적파일이니까 임시로 적어놨던 li지우고 실행하면 User테이블에 있는 데이터가 나온다.
html만들기
컨트롤러로 html 연결
엔티티
리파지토리
서비스
dto
서비스랑 컨트롤러 연결
model에 데이터담기
html에 데이터 뿌리기
이해하면 사이트 간단히 만들 수 있다고 한다..^^