파일 2개로 Spring app 만들기

fart man·2025년 12월 31일
package com.hib;

import jakarta.persistence.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import java.util.List;

@SpringBootApplication
public class HibApplication {

    @Entity
    @Table(name = "memes")
    public static class Meme {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        @Column(length = 50, nullable = false)
        private String content;

        public Long getId() {
            return id;
        }

        public String getContent() {
            return content;
        }
    }


    @Service
    public static class MemeService {
        private final MemeRepo repo;

        public MemeService(MemeRepo repo) {
            this.repo = repo;
        }

        @Transactional
        public void createMeme(Meme meme) {
            repo.save(meme);
        }

        @Transactional
        public List<Meme> getMemes() {
            return repo.findAll();
        }
    }

    @RestController
    public static class MemeController {
        private final MemeService service;

        public MemeController(MemeService service) {
            this.service = service;
        }

        @PostMapping("/memes")
        public void createMeme(@RequestBody Meme meme) {
            service.createMeme(meme);
        }

        @GetMapping("/memes")
        public List<Meme> getMemes() {
            return service.getMemes();
        }
    }


    public static void main(String[] args) {
        SpringApplication.run(HibApplication.class, args);
    }

}
package com.hib;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MemeRepo extends JpaRepository<HibApplication.Meme, Long> {
}

신기하게도 작동을 잘한다. 예시를 올리거나 실험을 할 때 유용할거 같다.

여담

JPA는 spec상 argument를 안받는 protected거나 public인 constructor가 필요하다. 근데 그럼 이 코드는 왜 작동하는 걸까?

    @Entity
    @Table(name = "memes")
    public static class Meme {
        private Meme() {}

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        @Column(length = 50, nullable = false)
        private String content;

        public Long getId() {
            return id;
        }

        public String getContent() {
            return content;
        }
    }

@Entity가 Lombok처럼 생성자를 만들어준다는 얘기도 들었지만 그건 사실이 아닌거 같다. 우리가 private Meme() {} 으로 public이나 protected인 생성자가 만들어지는 것을 막았으니까.

실제로 DB에 query를 쏘는 hibernate 소스코드에 setAccessible(true)를 찾아보면 꽤 많은 군데에서 이사람들이 OOP의 기본원칙을 위반하는 것을 볼 수 있다 :D.

내 추측이지만 hibernate가 private이든 아니든 상관없이 constructor가 있으면 불러서 entity객체를 만드는 거 같다.

0개의 댓글