如何使用 SpringBoot + JPA 存储 PostgreSQL jsonb?
how to store PostgreSQL jsonb using SpringBoot + JPA?
我正在开发一个迁移软件,它将使用来自 REST 服务的未知数据。
我已经考虑使用 MongoDB 但我决定不使用它并使用 PostgreSQL。
阅读 this 后,我正在尝试使用 Spring JPA 在我的 Spring 启动应用程序中实现它,但我不知道要在我的 jsonb
中映射实体。
尝试了 this 但一无所知!
我在这里:
@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 );
}
}
和 table:
CREATE TABLE public.dnit
(
id integer NOT NULL,
data jsonb,
CONSTRAINT dnit_pkey PRIMARY KEY (id)
)
我该怎么做?
注意:我 want/need 没有要处理的实体。我的 JSON 永远是 String 但我需要 jsonb 来查询 DB
您添加 Spring Data JPA 只是为了执行一个简单的插入语句,这让事情变得过于复杂。您没有使用任何 JPA 功能。而是执行以下操作
- 将
spring-boot-starter-data-jpa
替换为spring-boot-starter-jdbc
- 删除您的
DnitRepository
界面
- 注入
JdbcTemplate
你在哪里注入DnitRepository
- 将
dnitRepository.insertdata(2, someJsonDataAsString );
替换为jdbcTemplate.executeUpdate("insert into dnit(id, data) VALUES (?,to_json(?))", id, data);
您已经在使用纯 SQL(以一种非常复杂的方式),如果您需要纯 SQL(并且不需要 JPA),那么只需使用 SQL .
当然,您可能希望将 logic/complexity 隐藏在存储库或服务中,而不是直接将 JdbcTemplate
注入您的控制器。
Tried this but understood nothing!
在 Spring Data JPA (Hibernate) 项目中与 Vlad Mihalcea 的 jsonb
充分合作 hibernate-types lib 你应该只做以下事情:
1) 将此库添加到您的项目中:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.2.2</version>
</dependency>
2) 然后在你的实体中使用它的类型,例如:
@Data
@NoArgsConstructor
@Entity
@Table(name = "parents")
<b>@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)</b>
public class Parent implements Serializable {
@Id
@GeneratedValue(strategy = SEQUENCE)
private Integer id;
@Column(length = 32, nullable = false)
private String name;
<b>@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")</b>
private List<Child> children;
<b>@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")</b>
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();
对于这种情况,我使用上面定制的转换器class,您可以随意将其添加到您的库中。它与 EclipseLink JPA 提供程序一起工作。
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
class,例如:
public class Rule {
@Id
private String client_id;
private String username;
private String password;
private Json publish_acl;
private Json subscribe_acl;
}
已经有几个答案,我很确定它们适用于多种情况。我不想再使用任何我不知道的依赖项,所以我寻找另一种解决方案。
重要的部分是 AttributeConverter 它将 jsonb 从数据库映射到您的对象,反之亦然。因此,您必须使用 @Convert 和 link 您的 AttributeConverter[ 注释实体中 jsonb 列的 属性 =42=] 并添加 @Column(columnDefinition = "jsonb") ,因此 JPA 知道数据库中的类型。这应该已经可以启动 spring 引导应用程序。但是,每当您尝试使用 JpaRepository 进行 save() 时,都会遇到问题。我收到消息:
PSQLException: ERROR: column "myColumn" is of type jsonb but
expression is of type character varying.
Hint: You will need to rewrite or cast the expression.
发生这种情况是因为 postgres 对类型有点认真。
您可以通过更改配置来解决此问题:
datasource.hikari.data-source-properties: stringtype=unspecified
datasource.tomcat.connection-properties: stringtype=unspecified
之后它对我来说就像一个魅力,这里是一个最小的例子。
我使用 JpaRepositories:
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
);
还有我的application.yml (application.properties)
datasource:
hikari:
data-source-properties: stringtype=unspecified
tomcat:
connection-properties: stringtype=unspecified
我正在开发一个迁移软件,它将使用来自 REST 服务的未知数据。
我已经考虑使用 MongoDB 但我决定不使用它并使用 PostgreSQL。
阅读 this 后,我正在尝试使用 Spring JPA 在我的 Spring 启动应用程序中实现它,但我不知道要在我的 jsonb
中映射实体。
尝试了 this 但一无所知!
我在这里:
@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 );
}
}
和 table:
CREATE TABLE public.dnit
(
id integer NOT NULL,
data jsonb,
CONSTRAINT dnit_pkey PRIMARY KEY (id)
)
我该怎么做?
注意:我 want/need 没有要处理的实体。我的 JSON 永远是 String 但我需要 jsonb 来查询 DB
您添加 Spring Data JPA 只是为了执行一个简单的插入语句,这让事情变得过于复杂。您没有使用任何 JPA 功能。而是执行以下操作
- 将
spring-boot-starter-data-jpa
替换为spring-boot-starter-jdbc
- 删除您的
DnitRepository
界面 - 注入
JdbcTemplate
你在哪里注入DnitRepository
- 将
dnitRepository.insertdata(2, someJsonDataAsString );
替换为jdbcTemplate.executeUpdate("insert into dnit(id, data) VALUES (?,to_json(?))", id, data);
您已经在使用纯 SQL(以一种非常复杂的方式),如果您需要纯 SQL(并且不需要 JPA),那么只需使用 SQL .
当然,您可能希望将 logic/complexity 隐藏在存储库或服务中,而不是直接将 JdbcTemplate
注入您的控制器。
Tried this but understood nothing!
在 Spring Data JPA (Hibernate) 项目中与 Vlad Mihalcea 的 jsonb
充分合作 hibernate-types lib 你应该只做以下事情:
1) 将此库添加到您的项目中:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.2.2</version>
</dependency>
2) 然后在你的实体中使用它的类型,例如:
@Data
@NoArgsConstructor
@Entity
@Table(name = "parents")
<b>@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)</b>
public class Parent implements Serializable {
@Id
@GeneratedValue(strategy = SEQUENCE)
private Integer id;
@Column(length = 32, nullable = false)
private String name;
<b>@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")</b>
private List<Child> children;
<b>@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")</b>
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();
对于这种情况,我使用上面定制的转换器class,您可以随意将其添加到您的库中。它与 EclipseLink JPA 提供程序一起工作。
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
class,例如:
public class Rule {
@Id
private String client_id;
private String username;
private String password;
private Json publish_acl;
private Json subscribe_acl;
}
已经有几个答案,我很确定它们适用于多种情况。我不想再使用任何我不知道的依赖项,所以我寻找另一种解决方案。 重要的部分是 AttributeConverter 它将 jsonb 从数据库映射到您的对象,反之亦然。因此,您必须使用 @Convert 和 link 您的 AttributeConverter[ 注释实体中 jsonb 列的 属性 =42=] 并添加 @Column(columnDefinition = "jsonb") ,因此 JPA 知道数据库中的类型。这应该已经可以启动 spring 引导应用程序。但是,每当您尝试使用 JpaRepository 进行 save() 时,都会遇到问题。我收到消息:
PSQLException: ERROR: column "myColumn" is of type jsonb but expression is of type character varying.
Hint: You will need to rewrite or cast the expression.
发生这种情况是因为 postgres 对类型有点认真。 您可以通过更改配置来解决此问题:
datasource.hikari.data-source-properties: stringtype=unspecified
datasource.tomcat.connection-properties: stringtype=unspecified
之后它对我来说就像一个魅力,这里是一个最小的例子。 我使用 JpaRepositories:
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
);
还有我的application.yml (application.properties)
datasource:
hikari:
data-source-properties: stringtype=unspecified
tomcat:
connection-properties: stringtype=unspecified