Storing entire object as json in Spring Data - spring-data

I have the following domain object:
#Data
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String first;
private String last;
private Integer age;
private String json;
}
and a PersonRepository extends Repository<Person, Long>. What I would like is to store in a table that looks like this:
CREATE TABLE IF NOT EXISTS person
(
id SERIAL NOT NULL PRIMARY KEY,
json JSONB NOT NULL
);
Is there a way to override the way Spring Data reads and writes to/from the database? Ideally I'd use Jackson to convert the object to/from JSON.
Right now any operation complains that the fields on Person don't have a corresponding column in the table.

You can use hibernate-types lib by Vlad Mihalcea - one of Hibernate authors:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.1.1</version>
</dependency>
Then you can update your entity like this:
#Data
#Entity
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//...
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb", nullable = false)
private String json;
}
IMO it's better to use concrete object instead of String here, like this:
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb", nullable = false)
private SomeObject json;
More info: 1, 2

Related

JPA how to set foreign id - in Embeddable id - that is generated

My use case is: I have a Product with multi language name. To have at most one name per language a translation should be identified by locale + productId.
My problem is to get it working with a generated productId. This are the entities:
e.g. in oracle i get: "ORA-01400: cannot insert null into ."PRODUCT_NAME"."FOREIGN_ID""
Product:
#Entity
#Data
public class Product {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String articleNumber;
/**
* Localized name of the product.
*/
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#MapKey(name = "nameId.locale")
private Map<Locale, ProductName> names = new HashMap<>();
}
ProductName:
#Entity
#Data
public class ProductName {
#EmbeddedId
private TranslationId nameId;
private String name;
}
TranslationId:
#Embeddable
#Data
public class TranslationId implements Serializable {
private static final long serialVersionUID = 3709634245257481449L;
#Convert(converter = LocaleConverter.class)
private Locale locale;
private Long foreignId; //<-- this is null, should reference product
}
Is there a way to get this working without having to save the product first without the name? Without a generated id it is working of course - i just set same id for both.
I would like to re-use the translation id for other translated fields of other entities.

Spring Data Jpa OneToMany save bidirectional

I have a problem with saving child entities.
Here is my example. My model classes look like this:
#Entity
public class ImportDocument {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private boolean imported;
#Transient
private Status status;
#Basic
private char statusValue;
#OneToMany(mappedBy = "importDocument" , cascade = {CascadeType.ALL})
private List<ImportDocumentItem> importDocumentItems;
}
#Entity
public class ImportDocumentItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "import_document_id")
#JsonIgnore
private ImportDocument importDocument;
}
I have implemented JpaRepository interfaces for both domain classes.
I try to save with:
importDocumentRepository.save(importDocument);
When I save ImportDocument object, everything is inserted. But the problem is that, the import_document_item.import_document_id (which is foreign key of import_document_id) attribute is filled with null value, not with id of import_document that I expected. How can I fix this issue?
Thanks a lot.
You have to set entity relations on both side before saving. Here an example
ImportDocument importDocument = new ImportDocument();
//...
importDocument.setImportDocumentItems(items);
items.forEach(ImportDocumentItem::setImportDocument);
importDocumentRepository.save(importDocument);

How to retrieve nested rest data resources generated by #RepositoryRestResource annotation?

I have to entities exposed by spring boot application powered by Spring data REST.
#Entity
#Table(name = "joke")
#Data
public class Joke {
#Id
#Column(name = "joke_id")
private Long id;
#Column(name = "content")
private String content;
#JsonProperty("category")
#JoinColumn(name = "category_fk")
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
}
and category
#Entity
#Table(name = "category")
#Data
public class Category {
#Id
#Column(name = "category_id")
private int id;
#Column(name = "name")
private String name;
}
It is working fine and exposing the HAL+Json format. I'm using Traverson client which is working fine:
Traverson client = new Traverson(URI.create("http://localhost:8080/api/"),
MediaTypes.HAL_JSON);
HashMap<String, Object> parameters = Maps.newHashMap();
parameters.put("size", "2");
PagedModel<JokesDTO> jokes = client
.follow("jokes")
.withTemplateParameters(parameters)
.toObject(new PagedModelType<JokesDTO>() {
});
return jokes;
where JokesDTO is:
#Builder(toBuilder = true)
#Value
#JsonDeserialize(builder = JokesDTO.JokesDTOBuilder.class)
#JsonInclude(Include.NON_NULL)
public class JokesDTO {
private String content;
#JsonPOJOBuilder(withPrefix = "")
#JsonIgnoreProperties(ignoreUnknown = true)
public static class JokesDTOBuilder {
}
}
I'm new in HAL and HateOS and I would like to achieve 2 things (and question is - is it possible, and how):
Base on Traverson client call, how to retrieve category (or link to category) in one call? How to extend what I wrote. And I'm not talking about adding additional #JsonProperty annotation to my class definition.
Is it possible to expose the inner query from Spring data REST, so I would be able to get all data with one call, is it possible with #RepositoryRestResource?

Mapping A List of Enum to A Field in PostgreSQL With Hibernate/Spring Data

There are quite many samples of mapping an array or list of a primitive data type such as integer or string to a database field in PostgreSQL with Hibernate. I am wondering whether it is possible to map a list of Enum to a database field without an additional DB table in PostgreSQL with Hibernate or not. Here is one of use case.
public enum Language {
EN, FR, ZH
}
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private long id;
private List<Language> languages ;
...
}
If you want to map enum list in PostgreSQL specific way you can do the following:
Add hibernate-types lib to your project:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.1.1</version>
</dependency>
Add an enum type languages to your PostgreSQL database:
create type languages as enum (
'EN', 'FR', 'ZH'
);
Map your enum collection with this type:
#Entity
#Table(name = "person")
#TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType.class)
public class Person {
//...
#Enumerated(STRING)
#ElementCollection
#CollectionTable(name = "person_languages",
joinColumns = #JoinColumn(name = "person_id"),
foreignKey = #ForeignKey(name = "fk_person_languages_person"))
#Column(name = "language", columnDefinition = "languages", nullable = false)
#Type(type = "pgsql_enum")
private List languages;
}
More info about hibernate-types lib see on the its author, Vlad Mihalcea, site.
If you don't need the PostgreSQL specific way you can map your enum list just like this:
#Entity
#Table(name = "person")
public class Person {
//...
#Enumerated(STRING)
#ElementCollection
#CollectionTable(name = "person_languages",
joinColumns = #JoinColumn(name = "person_id"),
foreignKey = #ForeignKey(name = "fk_person_languages_person"))
#Column(name = "language", length = 2, nullable = false)
private List<Language> languages;
}

Exception while persisting JPA object in DB having one to many relation

hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work