How can I make some change on spring data underlying query - postgresql

I am using spring data in my project with Postgres as the Database. I have an entity that has a JSON field and I want to keep it as a String in the java context, but a jsonb in the database. Whenever I want to persist an instance of my entity the problem arises since hibernate is treating the JSON property as a Basic type and the underlying query does not change the String type to jsonb type.
#Entity
#Table(name = "a", catalog = "")
#TypeDef(name = "jsonb", typeClass = String.class)
public class A {
#Type(type = "jsonb")
#Column(name = "json-data", columnDefinition = "jsonb")
private String myJsonData;
}
I find the solution for this problem here, but it needs to change the parameters of the underlying PreparedStatement query. So, I need to change the query and add ::jsonb to make Postgres cast the parameter to jsonb. Is there any Annotation related to #Type to make it work?

You can use #ColumnTransformer for this purpose.

Related

Hibernate OGM with MongoDB could not resolve property

This HQL query give me error:
String q4 = "SELECT i, COUNT(ie) FROM CorsoStudi cs \n"
+ "\t JOIN cs.inserogati ie JOIN ie.insegn i \n"
+ "\t WHERE cs.nome = 'Laurea in Informatica' \n"
+ "\t GROUP BY i";
The error is:
Exception in thread "main" java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: insegn. of: component[_id,annierogazione,annoaccademico,crediti,discriminante,discriminantemodulo,hamoduli,id_facolta,insegn,inserogato_padre,modulo,nomemodulo,nomeunita,programma]
This is InsErogato:
#Embeddable
public class InsErogato {
private Integer _id;
private String annoaccademico;
#Embedded
private Insegn insegn;
#Embedded
private Discriminante discriminante;
private Integer modulo;
private String discriminantemodulo;
private String nomemodulo;
private Double crediti;
private String programma;
private Integer id_facolta;
private String hamoduli;
#Embedded
private InsErogatoPadre inserogato_padre;
private String nomeunita;
private Integer annierogazione;
// constructors, getters and setters and toString
}
and this is Insegn:
#Embeddable
public class Insegn {
private Integer _id;
private String nomeins;
private String codiceins;
// constructors, getters and setters and toString
}
Main:
// begin transaction
entityManager.getTransaction().begin();
List<Object[]> insegn = entityManager
.createQuery(q4, Object[].class)
.getResultList();
for(Object[] i : insegn) {
Insegn ins = (Insegn)i[0];
Long count = (Long)i[1];
System.out.println("nomeins: " + ins.getNomeins() + ", numero inserogati: " + count);
}
// commit transaction
entityManager.getTransaction().commit();
The MongoDB structure:
https://i.stack.imgur.com/qFusC.jpg
https://i.stack.imgur.com/k04HK.png
https://i.stack.imgur.com/H8nhS.png
https://i.stack.imgur.com/eYl2M.png
I tried to change the query but Hibernate doesn't find "insegn" (and also "discriminante") property in "inserogato", but he can find other simple attributes from it (like "annoaccademico" etc.).
The same query works on Hibernate ORM with PostgreSQL.
Maybe I have to add something in the annotations, or change the mongoDB structure (?).
I'm using Hibernate OGM 5.3.1.Final and MongoDB 3.6.3 JDBC Driver.
The same query works on Hibernate ORM with PostgreSQL
But PostgreSQL is a SQL database, MongoDB is a NoSQL database. Hibernate OGM is a means to add Hibernate ORM to NoSQL databases. The '(H)SQL' for OGM is limited (see below).
You don't say what your application is deployed on. I use WildFly 12.0.0.Final. I've had OGM with MongoDB working on version 11 & 12.
entityManager.getTransaction().begin();
I use the (WildFly) container to handle transactions. Annotate my EJB.
#TransactionManagement(TransactionManagementType.CONTAINER)
I don't believe you can use (H)SQL per se with Hiberate OGM but:
Use JPQL - only for simple queries for now
Use the NoSQL native query mapping the result as managed entities
Use Hibernate Search queries - primarily full-text queries
It says in the documentation:
In particular and of notice, what is not supported is:
cross entity joins
JPQL functions in particular aggregation functions like count
JPQL update and delete queries
One of my queries:
Query query = mongoDBEntityManager.createQuery("FROM FoodsCosmeticsMedicines f WHERE f.ean = :ean")
.setParameter("ean", ean);
The entity (the #Expose are for the JSON)
#Entity(name = "FoodsCosmeticsMedicines")
#Indexed
#Table(name = "foodsCosmeticsMedicines")
public class FoodsCosmeticsMedicines implements Serializable {
// Arrays of Objects
#Expose(deserialize = true, serialize = true)
#Embedded
ProductCharacteristics productCharacteristics;
#Expose(deserialize = true, serialize = true)
#Embedded
CalcNutrition calcNutrition;
#Expose(deserialize = true, serialize = true)
#Embedded
Nutrients nutrients;
#Expose(deserialize = true, serialize = true)
#Embedded
Enumbers enumbers;
#Expose(deserialize = true, serialize = true)
#Embedded
ChemicalsMineralsVitamins chemicalsMineralsVitamins;
#Expose(deserialize = true, serialize = true)
#Embedded
Lifestyle lifestyle;
.....
}
Why are you using JOIN?
With MySQL I use Hibernate as my ORM I would use annotations to map relationships with entities such as:
#OneToOne(cascade = CascadeType.ALL, mappedBy = "product", fetch = FetchType.LAZY)
private UriEntity uri;
entityManager.getTransaction().commit();
This is irrelevant as all you've done is read. Maybe you excluded the persist?
I'm sure you've read the documentation. I found all the answers to the problems I encountered here:
Hibernate OGM 5.3.1.Final: Reference Guide
Currently, queries with group by are not supported. You will need to run a native MongoDB query for this particular use case. See the reference documentation for more details about it.
I think the join on an embedded entity instead of a collection is confusing the parser even if it's a valid query. You should see a clearer exception with the following one (equivalent to the one you are trying):
SELECT ie.i, COUNT(ie)
FROM CorsoStudi cs
JOIN cs.inserogati ie
WHERE cs.nome = 'Laurea in Informatica'
GROUP BY ie.i;
It should throw:
java.lang.UnsupportedOperationException: The GROUP BY clause is not supported
Aggregate functions are not supported as well.

Storing ManyToMany with extra column using merge method

I have an entity class that is simply a ManyToMany with extra column, as follows:
#Entity
#Table(name = "view_templates_device_types")
public class ViewTemplateDeviceTypeEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "view_template_id")
private ViewTemplateEntity viewTemplate;
#Id
#ManyToOne
#JoinColumn(name = "device_type_id")
private DeviceTypeEntity deviceType;
#Column(name = "priority", nullable = false)
private int priority;
public ViewTemplateDeviceTypeEntity() {
}
...
}
I noticed that when I create a new object of this type, set viewTemplate and deviceType to values that have corresponding data in the db and I call entityManager.persist(entity) the data is stored. But when I call entityManager.merge(entity) instead of persist I get an exception:
SQL Error: 1048, SQLState: 23000
Column 'view_template_id' cannot be null
I thought that calling merge should result with data inserted into database in case it is not stored yet. It is quite important to me to use merge here (because of cascades). What can I do to make it work?
As per the JPA spec, section 2.4
"A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId or IdClass annotation is used to denote a composite primary key. See Sections 11.1.17 and 11.1.22.".
So you either need #IdClass or #EmbeddedId. Anything else is non-portable and prone to error. I am very surprised of any JPA provider that does not throw out warnings for this.

spring data query filter on property not on field

I have a pojo that contains this field:
#Transient
private EventType eventType;
where EventType in an enum:
To persist this field I use the property access:
#Access(AccessType.PROPERTY)
#Column(name = "EVENT_CODE")
String getEventTypeCode() {
return eventType.getEventCode();
}
void setEventTypeCode(String eventCode) {
eventType = EventType.decode(eventCode);
}
I need to filter on that property but with spring data I don't know how to do it.
Is it possible?
Massimo
You can look into Spring Data Specifications. You use them by calling repository.findAll(s);
Specifications allow you add on arbitrary conditions to your query, including the filter you want to add. See here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications

Why won't my EJB Entity work with a non-relational table, when using createNativeQuery?

I'm trying to use entities with a MySQL ndbcluster table. This table type doesn't allow foreign keys, but up until now it hasn't been a problem with my entities.
However, I have run into a bit of a problem, when I try to load an entity using the EntityManager's createNativeQuery method. I need to use this method due to my inability to do this: How to make a CriteriaBuilder join with a custom "on" condition?
My MySQL table looks like this:
CREATE TABLE `category` (
`id` SMALLINT(6) NOT NULL AUTO_INCREMENT,
`category_id` SMALLINT(6) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `category_id` (`category_id`)
)
COLLATE='utf8_general_ci'
ENGINE=ndbcluster
ROW_FORMAT=DEFAULT
If I change the table engine to innodb, and add foreign keys, the createNativeQuery method works fine.
My entity class looks like this:
#Entity
#Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Short id;
#OneToMany(mappedBy = "categoryId")
private List<Category> categoryList;
#JoinColumn(name = "category_id", referencedColumnName = "id")
#ManyToOne
private Category categoryId;
public Category() {
}
// getters and setters
}
Even without foreign keys, this entity works fine when I use the CriteriaBuilder for a query, but unfortunately not everything is possible with the CriteriaBuilder.
I get an error when I call getResultList on a Query object created with createNativeQuery. I don't know if this is a bug, or if something should be added to my entity class to make this work.
The error says:
Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
=> 2519
=> 2463
=> Tools)] during the execution of the query was detected to be null. Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Category sql="select * from `category`")
at org.eclipse.persistence.exceptions.QueryException.nullPrimaryKeyInBuildingObject(QueryException.java:895)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:584)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:560)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:717)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:769)
...
My table contains 1 row, where id=1 and category_id=null, so there are no primary keys with a null-value, despite what the error says. If I remove that row or set category_id=1, I don't get an error.
Need help, please.
Managed to make it work by switching from EclipseLink (JPA 2.0) to OpenJPA (JPA 2.0). Seems like there is a bug somewhere in EclipseLink 2.3.2 and/or GlassFish 3.1.2.2.
I've used EclipseLink (JPA 2.0) in another project of mine, using a slightly different version Netbeans + GlassFish 3.1.1, where I used createNativeQuery on an entity class for a non-relational myisam table. This never caused any problem. It really must be a bug.
But problem solved. Bye, bye EclipseLink, hello OpenJPA.
The issue is case sensitivity. In MySQL your column "id" will be defined in the database as "ID" unless you quote it. If you switch your mappings to upper case it should fix the issue (i.e. "ID").
You could also quote the column name ("'id'")
or set the persistence unit property,
"eclipselink.jpa.uppercase-column-names"="true"

named native query and mapping columns to field of entity that doesn't exist in table

I have a named native query and I am trying to map it to the return results of the named native query. There is a field that I want to add to my entity that doesn't exist in the table, but it will exist in the return result of the query. I guess this would be the same with a stored proc...
How do you map the return results of a stored proc in JPA?...
How do you even call a stored proc?
here is an example query of what I would like to do...
select d.list_id as LIST_ID, 0 as Parent_ID, d.description from EPCD13.distribution_list d
The Result will be mapped to this entity...
public class DistributionList implements Serializable {
#Id
#Column(name="LIST_ID")
private long listId;
private String description;
private String owner;
private String flag;
#Column(name="PARENT_ID", nullable = true)
private long parentID;
}
parent ID is not in any table in my database. I will also need to use this entity again for other calls, that have nothing to do with this call, and that will not need this parent_id? Is there anything in the JPA standard that will help me out?
If results from database are not required for further manipulation, just for preview, you can consider using database view or result classes constructor expression.
If entities retrieved from database are required for further manipulation, you can make use of multiple select expression and transient fields.
Replace #Column annotation with #Transient annotation over parentID.
After retrieving multiple columns from database, iterate over results and manually set parentID.