Quarkus에서 JPA를 구성합니다.목위일

JPA는 자카르타 EE의 규범으로 데이터 접근층을 제어하는 데 쓰인다.가장 흔히 볼 수 있는 실현은Hibernate ORM이다.

헤아리다


기술을 선택하려면 균형이 필요하다.만약 내가 JPA를 선택한다면, 나는 생성된 SQL에 대한 통제를 잃게 될 것이라고 생각해 왔다.이거 진짜야!
JPA는 ORM의 일종이기 때문에 대부분의 SQL은 JPQL에서 생성된다.만약 코드에 대해 엉터리 디자인을 한다면, 실행할 때 SQL을 생성할 것이다. 이것은 엉터리 디자인이다.사실 무서운 디자인.
하지만 너는 너의 인코딩 속도를 높일 것이다.SQL 또는 데이터베이스 모델링 작성에는 더 이상 관심이 없습니다.너는 표가 아니라 대상으로 생각할 수 있다.이것은 결코 네가 모델링에 무관심할 것이라는 것을 의미하지 않는다.가장 먼저 데이터베이스 모델을 만드는 것이 필요에 따라 디자인 대상을 만드는 것이 좋다.이렇게 하면 JPA에서 매우 일반적인 반모드를 생성하는 것을 방지할 수 있습니다.1. 한 항목에 참여한 적이 있다. 만약에 데이터베이스에서 사용자를 얻으면 이 사용자와 관련된 모든 대상을 메모리에 불러와 응용 프로그램이 붕괴될 수 있다.
가장 중요한 것은 최상의 서비스를 만들고 데이터베이스 성능과 개발 속도를 균형 있게 하려면 반드시 규범을 이해해야 한다는 것이다.
우세하다
AIS 이점
빠른 인코딩
SQL 생성
모델을 쉽게 변경할 수 있습니다.
NosQL에 대한 지원 부족
데이터베이스 불가지론
JPA 대신 JNoSQL 사용

기존 Quarkus 프로젝트에서 JPA 구성


종속성 구성


따라서 첫 번째 단계는 의존항을 설정하는 것이다.JPA를 추가하려면 Hibernate 플러그인과 데이터베이스 JDBC 플러그인을 추가해야 합니다.
왜 우리는 두 가지 의존 관계가 있습니까?이는 데이터베이스와 무관한 JPA의 코드 마인드 중 하나다.코드를 거의 바꾸지 않은 상태에서 데이터베이스를 변경할 수 있습니다.
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
어떤 데이터베이스를 사용할 수 있는지 알고 싶으면 검색mvnrepository.com만 하면 된다.

데이터베이스 모델링


다음 단계에 우리는 데이터베이스 모델링을 해야 한다.SQL을 사용하는 데이터베이스 모델이 있을 수 있지만, 자바 POJO를 만들어서 자바 코드에 데이터를 비추어야 합니다.
이 예에서는 사용자를 위한 백엔드 CRUD를 생성합니다.이것은 우리가 그것을 위해 창설, 읽기, 업데이트, 삭제 단점을 만들 것을 의미한다.
따라서 모델링을 위해 주석을 추가해야 합니다.
메모
묘사javax.persistence.Entity지정한 클래스는 실체입니다.javax.persistence.Table주석 솔리드의 마스터 테이블을 지정합니다.javax.persistence.Id엔티티의 기본 키를 지정합니다.javax.persistence.Column영구 속성이나 필드의 맵을 지정합니다.
심지어 모든 주석이 필요한 것은 아니지만, 당신은 코드의 설계를 잘 해야 합니다.
따라서, 나는 사용자가 필요한 모든 주석을 추가하고, 일부 NamedQuery와 생성기를 추가하는 것을 정의한다.생성기는 읽기 쉽고 이름이 붙는 검색을 위한 화려한 코드일 뿐이며, 이따가 사용자를 검색하는 데 사용될 것이다.
package io.vepo.tutorial.quarkus.user;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "tb_users", uniqueConstraints = {
    @UniqueConstraint(name = "uq_users_username", columnNames = {
        "username" }),
    @UniqueConstraint(name = "uq_users_email", columnNames = {
        "email" })
})
@NamedQuery(name = "User.findByUsernameAndHashedPassword", query = "SELECT usr FROM User usr WHERE usr.username = :username AND usr.hashedPassword = :hashedPassword AND usr.enabled = true")
@NamedQuery(name = "User.findByUsername", query = "SELECT usr FROM User usr WHERE usr.username = :username")
public class User {
    public static final class UserBuilder {
        private String email;
        private String username;
        private String firstName;
        private String lastName;
        private boolean admin;
        private String hashedPassword;

        private UserBuilder() {
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public UserBuilder username(String username) {
            this.username = username;
            return this;
        }

        public UserBuilder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public UserBuilder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public UserBuilder admin(boolean admin) {
            this.admin = admin;
            return this;
        }

        public UserBuilder hashedPassword(String hashedPassword) {
            this.hashedPassword = hashedPassword;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

    public static final UserBuilder builder() {
        return new UserBuilder();
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column
    private String email;

    @Column
    private String username;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column
    private boolean admin;

    @Column(name = "hashed_password")
    private String hashedPassword;

    @Column(name = "enabled")
    private boolean enabled;

    public User() {
    }

    private User(UserBuilder builder) {
        email = builder.email;
        username = builder.username;
        firstName = builder.firstName;
        lastName = builder.lastName;
        admin = builder.admin;
        hashedPassword = builder.hashedPassword;
        enabled = true;
    }

    public Integer getId() {
        return id;
    }

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

    public String getEmail() {
        return email;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public boolean isAdmin() {
        return admin;
    }

    public void setAdmin(boolean admin) {
        this.admin = admin;
    }

    public String getHashedPassword() {
        return hashedPassword;
    }

    public void setHashedPassword(String hashedPassword) {
        this.hashedPassword = hashedPassword;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        User other = (User) obj;
        if (id == null) {
            if (other.id != null) {
                return false;
            }
        } else if (!id.equals(other.id)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", email=" + email + ", username=" + username + ", firstName=" + firstName
                + ", lastName=" + lastName + ", admin=" + admin + ", hashedPassword=" + hashedPassword + ", enabled="
                + enabled + "]";
    }

}
나는 이 코드에 관한 중요한 내용을 지적하고 싶다.@Column 메모는 필수가 아니며 생략해도 JPA가 무시되는 것이 아니라 필드에서만 같은 이름의 열을 읽습니다.비지구화 열을 사용하려면, 창설 transient 필드를 사용해야 합니다.필드에 복합 이름lastName이 있는 경우 @Column에 이름이 지정되지 않은 경우 JPA는 lastName 대신 last_name를 검색합니다.나는 네가 이해했는지 모르겠지만, 명명은 매우 중요하다.모든 응용 프로그램은 명명 약정을 따라야 하며 SQL Naming ConvetionsJava Naming Convetions는 다르다.@Column가 있으면 당신은 더 많은 선택을 할 수 있습니다. 예를 들어 requiredunique 입니다.Jakarta Documentation에서 모든 옵션을 볼 수 있습니다.id란에 대해 나는 두 가지 중요한 주석을 첨가했다.@Id@GeneratedValue는 좋은 디자인을 만드는 데 필요한 것이지만 서로 다른 데이터베이스를 선택하면 값이 다를 수 있습니다!
나는 이미 유일한 제약을 추가했다. 이것은 필수적인 것이 아니지만, 내가 자동으로 데이터베이스를 만들 때, 나는 반드시 이렇게 해야 한다.미래의 강좌에서 Flyway를 사용하여 SQL을 사용하여 데이터베이스를 자동으로 만드는 방법을 가르쳐 드리겠습니다.

매개변수 정의


Hibernate는 아주 적은 인자만 설정하면 모든 인자를 넣을 수 있습니다 src/main/resources/application.properties.환경 변수를 사용하여 대체할 수도 있습니다.이 작업은 나중에 MicroFile Config 자습서에서 수행됩니다.
따라서 우리는 데이터베이스 형식과 증빙서류, 그리고 Hibernate가 어떻게 처리할 것인지를 정의해야 한다.생산의 경우 항상 quarkus.hibernate-orm.database.generation=validate를 사용하지만 우리의 예시에서 우리는 drop-and-create를 사용하여 매번 배치할 때마다 데이터베이스 모델을 다시 만들 것이다.
추가 구성 속성을 변경해야 하는 경우 Quarkus documentation has a list of all available configurations를 사용합니다.
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = postgres
quarkus.datasource.password = password
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/tutorial

quarkus.hibernate-orm.database.generation=drop-and-create

데이터베이스 만들기


데이터베이스를 만들려면 Docker를 사용합니다.그러니까 다음 명령만 수행하면
docker run --rm --name postgres-db -e POSTGRES_PASSWORD=password -e POSTGRES_DB=tutorial -p 5432:5432 -d postgres:13-alpine
Postgres를 직접 사용하고 싶으면 다운로드하고 설치하면 됩니다.그것은 아주 잘 쓸 것이다.

데이터베이스 액세스


데이터베이스에 접근할 수 있는 많은 방법이 있지만, 시종일관 사용해야 합니다 javax.persistence.EntityManager.나는 Users 이라는 서비스 클래스를 만들었고, 접근의 가장 좋은 방법을 보여 주는 방법을 추가했다.
package io.vepo.tutorial.quarkus.user;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.enterprise.context.ApplicationScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.transaction.Transactional;

@Transactional
@ApplicationScoped
public class Users {
    @PersistenceContext
    EntityManager em;

    public List<User> list() {
        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
        CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
        query.from(User.class);
        return em.createQuery(query).getResultList();
    }

    public User create(User user) {
        if (Objects.nonNull(user.getId())) {
            throw new IllegalStateException("Id should be null!");
        }
        em.persist(user);
        return user;
    }

    public Optional<User> findByUsername(String username) {
        TypedQuery<User> query = em.createNamedQuery("User.findByUsername", User.class);
        query.setParameter("username", username);
        return query.getResultStream().findFirst();
    }

    public User get(int userId) {
        return em.find(User.class, userId);
    }
}

표준 생성기 사용


쿼리를 동적으로 생성할 때 Criteria Builder를 사용하는 것이 좋습니다.질의를 프로그래밍 방식으로 만들지만 API는 매우 강력하기 때문에 이해하기가 쉽지 않습니다.

명명된 질의 사용


나에게 있어서, 이름 조회는commons 조회를 실행하는 가장 좋은 방법이다.SQL 대신 JPQL에서 질의를 작성해야 합니다.JPQL은 SQL과 매우 비슷하지만, 다른 데이터베이스에서 같은 조회를 사용할 수 있으며, Hibernate에서 번역해 줍니다.한 번만 컴파일할 수 있기 때문에 JPQL 질의를 사용자 정의하는 것보다 사용하는 것이 좋습니다.

질의 사용


질의를 직접 작성할 수 있습니다.하지만 이것은 좋은 방법이 아니다!호출할 때마다 createQuery 검색어를 컴파일합니다.
public Optional<User> findByUsername(String username) {
    TypedQuery<User> query = em.createQuery("SELECT usr FROM User usr WHERE usr.username = :username", User.class);
    query.setParameter("username", username);
    return query.getResultStream().findFirst();
}

직접 액세스 데이터


클래스와 Id가 있다면, 이 대상을 직접 방문할 수 있습니다.
public User get(int userId) {
    return em.find(User.class, userId);
}

사무 대상


트랜잭션 객체를 사용할 때 JPA 객체에서 변경한 내용은 모두 데이터베이스에서 업데이트됩니다.데이터베이스에서 읽은 모든 대상은 현재 세션에 추가됩니다.

실행 및 테스트


데이터베이스가 실행되면 마벤트를 사용하여Quarkus를 시작합니다.
mvn quarkus:dev
예를 들면 다음과 같은 세 가지 단점을 제공합니다.
  • 모든 사용자 목록: GET /user
  • 사용자 생성: POST /user
  • 사용자 이름으로 사용자를 찾습니다: GET /user/{username}
  • 아래의 저장소에서 모든 코드를 찾을 수 있습니다.

    웨이브 / 쿼크스 튜토리얼


    이것은 일련의 블로그 글입니다. 이 글에서Quakus 튜토리얼을 만들 것입니다.이오.


    쿼크스 튜토리얼


    층계


    1. REST API 만들기


    More information
    첫 번째 예에서는 Quarkus와 JAX-RS를 사용하여 가장 작은 REST API를 만들었습니다.

    2. JPA Jakarta 지속성 구성


    More information
    두 번째 예에서는 REST API에 지속성 레이어를 추가했습니다.

    3. Jakarta Bean 검증 구성


    More information
    세 번째 예에서는 REST API에 추가된 모든 레이어를 확인합니다.
    View on GitHub
    너는 우체부로 테스트할 수 있다.

    결론


    데이터베이스를 빠르게 개발하고 사용하려면 JPA를 사용해야 한다.조회를 걱정하지 않고 JPA의 특성과 실현을 이해하면 데이터베이스 접근을 최적화할 수 있습니다.

    미래 읽기

  • Catálogo de Code Smells ORM
  • 좋은 웹페이지 즐겨찾기