How to map some fields to their own columns and some to a single JSON column with JPA - jpa

We have a class that we want to store to a table, for example:
#Entity
public class SomeClass {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String fieldA;
private String fieldB;
private String fieldC;
}
Is there a way to map some of the fields, e.g. id and fieldA to specific columns and the others to a single JSON column so that the result would be:
mysql> select * from SomeClass;
+----+--------+--------------------------------+
| id | fieldA | otherFields |
+----+--------+--------------------------------+
| 1 | aaa | {"fieldB":"b1"} |
| 2 | bbb | {"fieldB":"b2", fieldC: "c2"} |
+----+--------+--------------------------------+
We don't want to move fieldB and fieldC to a separate class.
I've tried to use the #Convert annotation with an AttributeConverter but the AttributeConverter can only be used for a single column.

Related

Hibernate: how to auto generate an id that is a 36 base string

How do I go around auto generating an #Id that is not Integer/Long value, but rather a String which is a number in base 36?
e.g. if the next id is to be 58490, the String id attribute should be 194q.
#Entity
#Table(name = "persons")
public class Person {
#Id
//?
private String id;
#Column
private String name;
...
}
+--------+--------+
| base10 | base36 |
+--------+--------+
| ... | ... |
| 58490 | 194q |
| 58491 | 194r |
+--------+--------+
so that the actual persons table looks like:
+------+------+
| id | name |
+------+------+
| ... | ... |
| 194q | John |
| 194r | Jack |
+------+------+
You can create a custom annotation. Creating a custom annotation and put your logic in that annotation.
Another way is to write a converter :
public class DecimalToHexConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String attribute) {
// convert to hex
return object;
}
#Override
public String convertToEntityAttribute(String dbData) {
// convert back to decimal
return object;
}
}
Then apply this converter on your id.
#Id
#Convert(converter = DecimalToHexConverter.class)
private String id;

format sql query to postgresql query format in spring data jpa-jsonb column type

I have a table Employee with fileds id, name, age , Address(as Jsonb string stored in Postgres DB)
#Entity
#Table(name = "Employee")
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Employee implements Serializable {
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
int Id;
#Column
String name;
#Column
int age;
#Column(columnDefinition = "jsonb")
#Type(type="jsonb")
Address address; // this field is JSONB data stored in postgress DB
// setter and getters
}
public class Address implements Serializable {
String flatNo;
String area;
String state;
// setter and getters
}
DB table data format:
id | name | age | address
---+----------+-----+--------------------------------------------------------
20 | Peter | 16 | {"area": "Kanpur", "state": "KARNATAKA", "flatNo": "78/1"}
21 | Nani | 20 | {"area": "CTM", "state": "TA", "flatNo": "111"}
My query here is: I need to filter data based on jsonb field.Here is my query :
I have tried this with psql
testdb=# select * from employee where address ->> 'area' = 'Kanpur';
id | name | age | address
----+------+-----+-------------------------------------------------
21 | James | 20 | {"area": "Kanpur", "state": "KARNATAKA", "flatNo": "78/1"}
(1 row)
#Repository
public interface EmployeeRespository extends CrudRepository<Employee, Integer> {
#Query("select e from employee e where e.address ->> 'area' = :area")
public List findByArea(#Param("area") String age);
}
When i try with java code in spring data jpa, its an error shown:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: > near line 1,
column 57 [Select e from com.web.rest.Employee e where e.address ->> 'area' = :area]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74) ~
[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:297) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:155) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
area is not direct field of Employee object, how to map area to Address in query.
Please suggest.Thanks in advance.

JPA, Map entity Person which contains class of type Name

I have an entity Person which consists of a name Attribute
#Entity
public class Person {
// ...
//#Transient
private Name name;
// ...
}
I dont want to store "name" in an extra table... I mark name as transient so it is not stored in the underlying database.
What I want is to map the attribute "name" to columns "first_name" and "last_name" in the database.
For example I can create a person like new Person(new Name("John","Doe"));
How can I achieve a mapping that the underlying table contains two additional columns first_name and last_name and the contents are the strings which I get from the name attribute?
The table, based on the person entity should look like
id|first_name|last_name
1 |John | Doe
2 |Jane | Doe
You can use embeddables:
#Embeddable
public class Name {
private String firstName;
private String lastName;
// getters and setters
}
And then use it like.
#Entity
public class Person {
// ...
private Name name;
// ...
}
Read more about embeddables in the Hibernate documentation: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#embeddables

How to query jsonb column using spring data specification?

I have below table temp_tbl (postgres):
ID(int) NAME(TEXT) LINKS(jsonb)
-------- ---------- -------------------
1 Name1 ["1","23","3", "32]
2 Name2 ["11","3","31", "2]
3 Name3 ["21","13","3", "12]
Now my native query to get rows which has 'LINKS' with value as "3" is:
select * from temp_tbl where links #> '["3"]'
returns rows 2 & 3.
I want implement this query using org.springframework.data.jpa.domain.Specification
I have implemented something like below where the jsonb column is not-an-array, but has key-value json using jsonb_extract_path_text. But the above column stores only values in an array.
My entity class.
#Entity
#Table(name = "temp_tbl")
public class TempTbl {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "NAME", nullable = false)
private String name;
#Column(name = "LINKS", columnDefinition = "jsonb null")
#Convert(converter = JsonbConverter.class)
private List<String> linkIds;
}
I need help in translating above query into specification using criteriabuilder.
One way to check if a jsonb array contains a String using Jpa specifications is to use the functions jsonb_contains and jsonb_build_array, the latter of which is used to change the String into a jsonb array for use in jsonb_contains.
public static Specification<TempTbl> linkInLinks(String linkId) {
return (root, query, builder) -> {
Expression toJsonbArray = builder.function("jsonb_build_array", String.class, builder.literal(linkId));
return builder.equal(builder.function("jsonb_contains", String.class, root.get("linkIds"), toJsonbArray), true);
};
}

Condition query on the join

I have a Rent class who contain a list a payment.
I would like to find to rent with paymentDue or with empty payment
So if a rent don't have any roompayment, i would like to get it.
Also if there are roomPayment and contain a paymentDueDate and the date in parameter is after the paymentDueDate, if there are not other roomPayment after it, i would like to get it.
Is it possible to do it in JPA or i need to get data and parse it with Java?
#Entity
public class Rent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long rentId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "rent")
private List<RoomPayment> roomPaymentList;
private LocalDate fromDate;
private LocalDate toDate;
...
}
#Entity
public class RoomPayment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long roomPaymentId;
#OneToOne
private PaymentType paymentType;
private BigDecimal amountReceived;
private LocalDate paymentDueDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "rent_id")
private Rent rent;
...
}
I created this query but it's not complete
#Query("select r from Rent r where r.roomPaymentList is empty and r.fromDate <= :date and r.toDate >= :date")
public List<Rent> findLateRentPayment(#Param("date") LocalDate date);
Edit
Actually, this query generate this
select
rent0_.rent_id as rent_id1_33_,
rent0_.bail_id as bail_id4_33_,
rent0_.from_date as from_dat2_33_,
rent0_.to_date as to_date3_33_
from
rent rent0_
where
not (exists (select
roompaymen1_.room_payment_id
from
room_payment roompaymen1_
where
rent0_.rent_id=roompaymen1_.rent_id))
and rent0_.from_date<=?
and rent0_.to_date>=?
If i have theses data
rent
rentId | fromDate | toDate
1 | 2015-12-08 | 2015-12-15
2 | 2015-12-08 | 2015-12-15
3 | 2015-12-08 | 2015-12-15
roompayment
roomPaymentId | rentId | paymentDueDate
1 | 1 |
3 | 3 | 2015-12-14
Query should return 2 and 3
2 because rent don't have any roomPaymentId
3 because have a roomPayment with paymentDueDate but they don't have any other roomPayment after this one
select r from Rent r
left join r.roomPaymentList payment
where payment.paymentDueDate < :date
or r.roomPaymentList is empty
It will return rents containing no payment and rents containing any payment which the due date is before a parameter date. Don't know if this is the result set that you want.