source

Postgre 저장 방법Spring Boot + JPA를 사용한SQL jsonb?

manysource 2023. 2. 16. 22:08

Postgre 저장 방법Spring Boot + JPA를 사용한SQL jsonb?

REST 서비스의 알 수 없는 데이터를 소비하는 이행 소프트웨어를 개발하고 있습니다.

이미 MongoDB를 사용할까 생각중이지만 사용하지 않고 Postgre를 사용하기로 했습니다.SQL.

이 글을 읽은 후 Spring JPA를 사용하여 Spring Boot 앱에 구현하려고 하는데 매핑을 할 수 없습니다.jsonb내 실체에 있어

시도해 봤지만 아무것도 이해하지 못했어!

여기가 제 위치입니다.

@Repository
@Transactional
public interface DnitRepository extends JpaRepository<Dnit, Long> {

    @Query(value = "insert into dnit(id,data) VALUES (:id,:data)", nativeQuery = true)
    void insertdata( @Param("id")Integer id,@Param("data") String data );

}

그리고...

@RestController
public class TestController {

    @Autowired
    DnitRepository dnitRepository;  

    @RequestMapping(value = "/dnit", method = RequestMethod.GET)
    public String testBig() {
        dnitRepository.insertdata(2, someJsonDataAsString );
    }

}

및 표:

CREATE TABLE public.dnit
(
    id integer NOT NULL,
    data jsonb,
    CONSTRAINT dnit_pkey PRIMARY KEY (id)
)

이거 어떻게 해?

주의: 작업하는 엔티티는 필요 없습니다.JSON은 항상 String이지만 DB를 쿼리하려면 jsonb가 필요합니다.

시도해 봤지만 아무것도 이해하지 못했어!

Spring Data JPA(Hibernate) 프로젝트에서 Vlad Mihalcea의 hibernate-types lib를 완전히 사용하려면 다음 작업을 수행합니다.

1) 이 lib를 프로젝트에 추가합니다.

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.2.2</version>
</dependency>

2) 다음으로 엔티티에서 예를 들어 다음과 같은 유형을 사용합니다.

@Data
@NoArgsConstructor
@Entity
@Table(name = "parents")
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Parent implements Serializable {

    @Id
    @GeneratedValue(strategy = SEQUENCE)
    private Integer id;

    @Column(length = 32, nullable = false)
    private String name;

    @Type(type = "jsonb")
    @Column(columnDefinition = "jsonb")
    private List<Child> children;

    @Type(type = "jsonb")
    @Column(columnDefinition = "jsonb")
    private Bio bio;

    public Parent(String name, List children, Bio bio) {
        this.name = name;
        this.children = children;
        this.bio = bio;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Child implements Serializable {
    private String name;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bio implements Serializable {
    private String text;
}

다음 예를 정도 하다'라는 있을 거예요.JpaRepository다음 중 하나:

public interface ParentRepo extends JpaRepository<Parent, Integer> {
}
parentRepo.save(new Parent(
                     "parent1", 
                     asList(new Child("child1"), new Child("child2")), 
                     new Bio("bio1")
                )
);
Parent result = parentRepo.findById(1);
List<Child> children = result.getChildren();
Bio bio = result.getBio();

단순한 삽입문을 실행하기 위해 Spring Data JPA를 추가함으로써 작업이 지나치게 복잡해지고 있습니다.JPA 기능을 사용하지 않습니다.대신 다음을 수행합니다.

  1. spring-boot-starter-data-jpaspring-boot-starter-jdbc
  2. DnitRepository
  3. 「」를 JdbcTemplate서 '주입'을 합니다.DnitRepository
  4. dnitRepository.insertdata(2, someJsonDataAsString );jdbcTemplate.executeUpdate("insert into dnit(id, data) VALUES (?,to_json(?))", id, data);

이미 플레인 SQL(매우 복잡한 방식)을 사용하고 있었기 때문에 플레인 SQL이 필요한 경우(JPA가 필요하지 않은 경우) 그냥 SQL을 사용하십시오.

직접 JdbcTemplate이러한 논리/복잡함을 저장소 또는 서비스에서 숨길 수 있습니다.

이미 몇 가지 답변이 있고 몇 가지 경우에 효과가 있을 것이라고 확신합니다.모르는 의존관계를 더 이상 사용하고 싶지 않기 때문에 다른 솔루션을 찾고 있습니다.중요한 부분은 AttributeConverter입니다.이 AttributeConverter는 jsonb를 DB에서 객체로 매핑하거나 그 반대로 매핑합니다.따라서 엔티티 내의 jsonb 컬럼 속성에 @Convert를 주석을 붙이고 AttributeConverter링크하고 @Column(Column Definition = "jsonb")도 추가해야 합니다.그러면 JPA는 DB에서 이것이 어떤 유형인지 알 수 있습니다.이것으로 스프링 부트애플리케이션을 기동할 수 있게 됩니다.그러나 JpaRepository에서 save()를 시도하면 문제가 발생합니다.메시지를 받았습니다.

PSQLException: ERROR: "myColumn" 열은 jsonb 유형이지만 식이 다양한 유형입니다.

힌트: 식을 다시 쓰거나 캐스팅해야 합니다.

이것은 postgres가 유형을 약간 심각하게 받아들이기 때문에 발생합니다.이 문제는 다음 변경으로 해결할 수 있습니다.

데이터 소스hikari.data-source-properties: string type=미지정

datasource.datas.connection-properties: stringtype=미지정

그 후, 그것은 마법처럼 나에게 효과가 있었고, 여기 최소한의 예가 있다.JpaRepository를 사용합니다.

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

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {
}

엔티티:

import javax.persistence.Column;
import javax.persistence.Convert;

public class MyEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  protected Integer id;

  @Convert(converter = MyConverter.class)
  @Column(columnDefinition = "jsonb")
  private MyJsonObject jsonContent;

}

json 모델:

public class MyJsonObject {

  protected String name;

  protected int age;

}

변환기는 여기서 Gson을 사용하지만 원하는 대로 매핑할 수 있습니다.

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = true)
public class MyConverter implements AttributeConverter<MyJsonObject, String> {

  private final static Gson GSON = new Gson();

  @Override
  public String convertToDatabaseColumn(MyJsonObject mjo) {
    return GSON.toJson(mjo);
  }

  @Override
  public MyJsonObject convertToEntityAttribute(String dbData) {
    return GSON.fromJson(dbData, MyJsonObject.class);
  }
}

SQL:

create table my_entity
(
    id serial primary key,
    json_content jsonb

);

그리고 my application.yml (application.properties)

  datasource:
    hikari:
      data-source-properties: stringtype=unspecified
    tomcat:
      connection-properties: stringtype=unspecified

이 경우, 저는 위의 맞춤형 컨버터 클래스를 사용하므로, 자유롭게 라이브러리에 추가할 수 있습니다.EclipseLink JPA Provider와 연동하고 있습니다.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.postgresql.util.PGobject;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;

@Converter
public final class PgJsonbToMapConverter implements AttributeConverter<Map<String, ? extends Object>, PGobject> {

    private static final Logger LOGGER = Logger.getLogger(PgJsonbToMapConverter.class);
    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public PGobject convertToDatabaseColumn(Map<String, ? extends Object> map) {
        PGobject po = new PGobject();
        po.setType("jsonb");

        try {
            po.setValue(map == null ? null : MAPPER.writeValueAsString(map));
        } catch (SQLException | JsonProcessingException ex) {
            LOGGER.error("Cannot convert JsonObject to PGobject.");
            throw new IllegalStateException(ex);
        }
        return po;
    }

    @Override
    public Map<String, ? extends Object> convertToEntityAttribute(PGobject dbData) {
        if (dbData == null || dbData.getValue() == null) {
            return null;
        }
        try {
            return MAPPER.readValue(dbData.getValue(), new TypeReference<Map<String, Object>>() {
            });
        } catch (IOException ex) {
            LOGGER.error("Cannot convert JsonObject to PGobject.");
            return null;
        }
    }

}

사용 예, 엔티티에 대한 이름Customer.

@Entity
@Table(schema = "web", name = "customer")
public class Customer implements Serializable {

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

    @Convert(converter = PgJsonbToMapConverter.class)
    private Map<String, String> info;

    public Customer() {
        this.id = null;
        this.info = null;
    }

    // Getters and setter omitted.

R2DBC를 사용하는 경우 종속성을 사용할 수 있습니다.io.r2dbc:r2dbc-postgresql, 및 타입을 사용합니다.io.r2dbc.postgresql.codec.Json다음과 같이 엔티티 클래스의 멤버 속성에 포함됩니다.

public class Rule {
    @Id
    private String client_id;
    private String username;
    private String password;
    private Json publish_acl;
    private Json subscribe_acl;
}

언급URL : https://stackoverflow.com/questions/51276703/how-to-store-postgresql-jsonb-using-springboot-jpa