I'd like to persist to jsonb type column (com.vladmihalcea.hibernate.type.json.JsonBinaryType)
#Type(type = "jsonb")
#Column(name = "style", columnDefinition = "jsonb")
private TextStyleJsonB style;
JSON object is relative big and i don't want that it saving fields with null values, so i decided to use custom object mapper, where i ignore null values. But it was not applied and all null values still saved to postgres database.
Any idea how to get rid of null fields and save only fields with real value?
In spring boot i have following application properties
hibernate:
types:
jackson:
object:
mapper: com.xxx.constructor.configurations.CustomObjectMapperSupplier
CustomObjectMapperSupplier looks like this:
public class CustomObjectMapperSupplier
implements ObjectMapperSupplier {
#Override
public ObjectMapper get() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
}
Instead of defining the CustomObjectMapperSupplier in your application.yml, you should create an hibernate.properties file with the property:
hibernate.types.jackson.object.mapper=com.xxx.constructor.configurations.CustomObjectMapperSupplier
Related
I am writing an API where I am inserting a record into a table (Postgres). I was hoping to use JPA for the work. Here is the potential challenge: the primary key for the insert is generated from a database trigger, rather than from sequence count or similar. In fact, the trigger creates the primary key using the values of other fields being passed in as part of the insert. So for example,
if I have a entity class like the following:
#Entity
#Validated
#Table(name = "my_table", schema="common")
public class MyModel {
#Id
#Column(name = "col_id")
private String id;
#Column(name = "second_col")
private String secCol;
#Column(name = "third_col")
private String thirdCol;
public MyModel() {
}
public MyModel(String id, String secCol, String thirdCol) {
this.id = id;
this.secCol = secCol;
this.thirdCol = thirdCol;
}
}
I would need the col_id field to somehow honor that the key is generated from the trigger, and the trigger would need to be able to read the values for second_col and third_col in order to generate the primary key. Finally, I would need the call to return the value of the primary key.
Can this be done with jpa and repository interface such as:
public interface MyRepo extends JpaRepository <MyModel, String> {
}
and then use either default save method such as myRepo.saveAndFlush(myModel) or custom save methods? I can't find anything on using JPA with DB triggers that generating keys. If it cannot be done with JPA, I would be grateful for any alternative ideas. Thanks.
ok, I was able to get this to work. It required writing a custom query that ignored the primary key field:
public interface MyRepo extends JpaRepository <MyModel, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(second_col, third_col)", nativeQuery = true)
int insertMyTable(#Param("second_col") String second_col, #Param("third_col") String third_col);
}
The model class is unchanged from above. Because it was executed as a native query, it allowed postGres to do its thing uninterrupted.
I have a user defined class CostMatrix this class contains two properties and data type for both properties is HashMap. I am using Morphia to communicate with mongodb so my entity class looks like this
#Entity(value = "CostMatrix",noClassnameStored = false)
public class CostMatrix {
#Id
private String id;
private HashMap<String,Double> distances;
private HashMap<String,Double> durations;
public CostMatrix(){}
public CostMatrix(String id, HashMap<String,Double>distances, HashMap<String,Double>durations) {
this.id = id;
this.distances = distances;
this.durations = durations;
}
I am unable to store object properly into database object is stored any how but when I retrieve its just returns id and class name any thoughts would be appreciated.
if you don't want to have any class/package names in your collection just put the noClassnameStored flag to true.
#Entity(value = "CostMatrix",noClassnameStored =**true**)
As for the saving part, do you fill some values into your maps? The mapper will ignore null values and empty lists.
I am tying to load an instance of the class "DataTable" from a mongo database by using the default codec registry (MongoClient.getDefaultCodecRegistry()) and the builder provided by the PojoCodecProvider. I have registered the DataTable class in the codec provider and the object is properly mapped from the database when the records field is null. Nevertheless, I get an error when the records property contains data. Furthermore, I need to have the records field defined as a list of objects with arbitrary attributes. Is it possible to use the default PojoCodecProvider for this purpose? Is there any other alternative?
import com.mongodb.BasicDBList;
import org.bson.types.ObjectId;
import java.util.List;
public class DataTable {
private ObjectId id;
private List<String> fields;
private BasicDBList records;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public List<String> getFields() {
return fields;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
public BasicDBList getRecords() {
return records;
}
public void setRecords(BasicDBList records) {
this.records = records;
}
}
The exception that I get when load an instance of the DataTable class is the following.
2018-03-21T16:32:04,526 [http-bio-8081-exec-4] ERROR ...service.controllers.BaseController - Failed to decode 'records'. Unable to set value for property 'records' in DataTable
org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'records'. Unable to set value for property 'records' in DataTable
at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:192) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:168) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:122) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:126) ~[bson-3.6.3.jar:?]
I get this exception when I try to load an item with the following code
DataTable item = collection.find(eq(new ObjectId(id))).first();
Well, one alternative you can use is Jackson Serialization.
I think something like this would suit you just fine
Document document = collection
.find(eq(new ObjectId(id)))
.first();
String json = document.toJson();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DataTable dataTable = mapper.readValue(json, DataTable.class);
See this question converting Document objects in MongoDB 3 to POJOS for reference
I have DAO implementation over spring-data:
public interface TestDataRepository extends CrudRepository<DpConfigData, Long> {
#Query(value = "select distinct(oid) from unit", nativeQuery = true)
List<Long> testMethod();
}
And unit test to test menioned DAO:
#Test
public void test(){
List<Long> testData = dpConfigDataEntityDataRepository.testMethod();
for (Long oid:testData){
System.out.print(oid);
}
}
Running test give strange result - List<Long> testData in runtime is populated by BigInteger instances, not by Long. As result I get ClassCastException: java.math.BigInteger cannot be cast to java.lang.Long
JPA implementation - Hibernate.
As DB I use PostgreSQL, unit.oid field has BigInt type on DB layer.
It is mapped to Long in case of fetching whole unit, but with custom query as "select distinct ..." something went wrong and it is mapped to BigInteger.
So, my question: what is the cause of such strange behaviour?
How to solve/workaround it in elegant way?
This is a issue with Spring data JPA.
If in DB the datatype is defined as BigInteger and in JPA query we tries to fetch as Long then it will not give any error , but it set value as BigInteger in Long datatype.
Solutions:
Use BigInteger as return type
#Query(value = "select distinct(oid) from unit", nativeQuery = true)
List<BigInteger> testMethod();
then set the variable as below.
Long variable = bigIntegerValue.longValue();
Use String as return Type and convert to Long
#Query(value = "select distinct(oid) from unit", nativeQuery = true)
List<String> testMethod();
then set the value as
Long variable = Long.valueOf(stringValue);
Change DB column type to Integer/Number.
Get the Value from Entity Object.
Long variable = dpConfigData.getOid();
where dpConfigData is object of Entity(DpConfigData.class)
BigInt in postgresql maps to BigInteger because its unsigned
I think your best option is to change oid from Long to BigInteger in your JPA object
Finally I worked around this problem by manual mapping on "service" layer.
Example(pseudo code):
public interface TestDataRepository extends CrudRepository<DpConfigData, Long> {
#Query(value = "select distinct(oid) from unit", nativeQuery = true)
List<Object> testMethod();
}
}
then in Service Layer I do manual mapping:
public class TestServiceImpl extends TestService {
pulic List<Object> testMethod(){
List<Object> rawList = testDataRepository.testMethod();
List<Object> resultList = new ArrayList(rawList.size());
for(Object rw:rawList){
resultList.add(Long.valueOf(String.valueOf(rw)));
}
return resultList;
}
}
This issue seems to be resolved in version 2.1.8
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
You can try this by using JPQL as below:
public interface TestDataRepository extends
JpaRepository<DpConfigData, Long> {
#Query(value = "select distinct(u.oid) from unit u")
List<Long> testMethod();
}
Just make sure that your Entity Object should also have same data type Long for the given attribute.
I have a simple JEE6 rest class that gets the data from db2. I am using Jackson in ApplicationConfig class to convert the entity objects to json. It converts with the field names as the key and the value as the right hand value. So for example:
Class Entity {
String name;
String address;
}
converts to
{name:"hello", address:"world"}
The service is as follows:
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
Now I want to only return the name in json format. So I created a named query as follows in the entity class:
#NamedQuery(name = "justGetName", query = "SELECT a.name FROM Applications a")
And the service changed to
public List<T> findAll() {
return getEntityManager().createNamedQuery("justGetName").getResultList();
}
This returns the following array:
[{"first","second","third"}]
But I want to get back:
[{name:"first",name:"second",name:"third"}]
How do I write the named query so that the class field names are added to the json structure? Thank you.
You querying a list of strings from your database and this is what the service returns.
Their are multiple ways to achieve your goal.
Pure JPA
Using #JsonIgnore to tell Jackson not to serialize an attribute
class Application {
String name;
#JsonIgnore
String address;
}
Create a new Entity class that only contains the attributes you would like to share
class ApplicationName {
String name;
}
Alternatively you could introduce a separate class that only contains the attributes you would like to share and convert the results from the query into this class and return than the list of this converted values.