Can I use Java 16 record with JPA entity? - java

I am trying to do something similar like below.
#Entity
#Table(name="Sample")
public record Sample(Integer id, String name) {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_id")
private Integer id;
#Column(name="username")
private String name;
}
However, it gives me error "User declared non-static fields id are not permitted in a record"
and same for name field as well.
Is there a way to use new java feature "record" with JPA annotation?

See the article, Using Records as Projections in JPA by Billy Korando. The following is a brief summary.
Records cannot be Entities
Jakarta Persistence (JPA; formerly Java Persistence API) implementations such as Hibernate depend on features either forbidden or not recommended by the JEP 395: Records spec: no-arg constructors, non-final fields, setters, etc.
➥ So, no, records cannot be used as JPA Entity.
Other uses of records
You can use records with:
CriteriaBuilder
TypedQuery
NativeQuery
Mapping definition
Spring data has some support as well.
See that article linked above for details, and for links to two other articles.

Related

How to map to an existing Hibernate model using jOOQ fetchInto()?

I'm trying to use the jOOQ fetchInto() method to map to an existing Hibernate model Organization (class and its inheritances are below).
Organization organization = jooq().select().from(ORGANIZATION).fetchOne().into(Organization.class);
The problem I have is that I can't really understand what happens in DefaultRecordMapper as I feel I'm not entirely familiar with all the terms that are used. I'm trying to figure out how it applies to the Hibernate classes that are in my codebase.
So far what I've tried:
Use the jOOQ generated POJO's to see if it retrieves and maps the data at all (works).
Add a constructor, getters and setters to the Organization Hibernate model.
Add #Column annotation to name in the Organization Hibernate model.
What works:
id field gets mapped correctly.
What doesn't work:
name field doesn't get mapped (null).
createdAt and modifiedAt fields do not get mapped (null).
My question is: Is there something I am overlooking with the mapping and what are the things I should look at concerning the classes, fields, constructors and annotations with Hibernate models? I want to eventually map all the Hibernate models in the codebase and use fetchInto to do that.
Thanks! :)
#Entity
public class Organization extends BaseModel {
#Required public String name;
//... a lot of other code
}
#MappedSuperclass
public class BaseModel extends Model {
/** The datetime this entity was first saved. Automatically set by a JPA prePersist */
#NoBinding
#Column
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime createdAt;
/** The datetime this entity was last modified. Automatically set by a JPA preUpdate */
#NoBinding
#Column
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime modifiedAt;
//...
}
#MappedSuperclass
public class Model extends GenericModel { // Both Model and GenericModel are from the Play Framework
#Id
#GeneratedValue
public Long id;
public Model() {
}
public Long getId() {
return this.id;
}
public Object _key() {
return this.getId();
}
}
jOOQ doesn't support all the many JPA and Hibernate specific annotations. Historically, it supported a few JPA annotations (because why not), but full interop would be excessive and investing product development time in the wrong places. jOOQ is by no means a JPA implementation.
Step 0: Why didn't (some) of the mappings work?
As mentioned before, not all JPA specification is implemented. For example, a known issue is that #Column annotations are still mandatory in jOOQ:
https://github.com/jOOQ/jOOQ/issues/4586
There might be other such limitations, which could be considered bugs. Feel free to report them if you want to continue down this path: https://github.com/jOOQ/jOOQ/issues/new/choose
But things like #MappedSuperclass or #Type are unlikely to ever be supported by jOOQ.
Step 1: Do you really need it?
You've decided to create and run your query with jOOQ. I imagine your actual query is much more complex than what you're showing, because for that particular query, you don't need jOOQ.
Do you really need to map to Hibernate entities? Because even when you use Hibernate, the recommended approach is to use entities only when you're going to modify them and store the delta back to the database. If that's the case, see step 2 below. If it's not the case, why not use jOOQ's own mapping functionality to work with any style of jOOQ supported POJO?
Step 2: Use Hibernate to execute the jOOQ query
If you're using jOOQ only to build a rather complex SQL query and you need Hibernate entities as a result, then use Hibernate to execute the jOOQ query as documented here. A small utility should be enough:
public static <E> List<E> nativeQuery(EntityManager em, org.jooq.Query query, Class<E> type) {
Query result = em.createNativeQuery(query.getSQL(), type);
List<Object> values = query.getBindValues();
for (int i = 0; i < values.size(); i++)
result.setParameter(i + 1, values.get(i));
return result.getResultList();
}

Multiple levels of cascading persists in Ebean

I have a model class which defines a list of children that are models of the same class. Persisting a new object with some initial children works fine, but when I have two or more levels of children Ebean does not seem to be able to handle it well. This seemed unexpected so I'm worried I made a mistake. At the same time I couldn't find any examples or mentions about multiple level persist cascades so my questions are:
Is there an error in my code, Is this even a supported feature or did I find a bug?
My model class:
#Entity
public class TestEntity extends Model {
#Id
private int id;
private String text;
#ManyToOne
private TestEntity parentEntity;
#OneToMany(cascade = CascadeType.ALL)
private List<TestEntity> childEntities;
...
}
My program:
TestEntity grandparent = new TestEntity();
grandparent.setText("grandparent");
TestEntity parent = new TestEntity();
parent.setText("parent");
TestEntity child = new TestEntity();
child.setText("child");
grandparent.setChildEntities(Collections.singletonList(parent));
parent.setChildEntities(Collections.singletonList(child));
grandparent.save();
I added logging for the sql statements and it is evident that the third insert didn't get the correct value for parent_entity_id. That row fails due to 0 not being a valid foreign key and the batch is reverted.
insert into test_entity (text, parent_entity_id) values ('grandparent',null);
insert into test_entity (text, parent_entity_id) values ('parent',1);
insert into test_entity (text, parent_entity_id) values ('child',0);
I'm using Play framework 2.7.3 with the ebean plugin version 5.0.2 and Ebean version 11.39
This is indeed a supported feature and the code snippet above is expected to persist all three entities.
There was a unit test added to verify that this is working correctly in the latest version of ebean.
In ebean 11.39 which is currently the latest supported by play framework the test fails. An easy workaround when using that version is to use Long instead of primitive int as ID for the models.
While not an answer to this specific question, it is good to be aware that these same symptoms also appear if the collections are set without using setters enhanced by ebean. I had some trouble using public fields and play enhancer .

How to enforce Entity Manager to use #SqlResultSetMapping and #NamedNativeQuery for fetching data?

I have entity class "Geo" to store geospatial data which uses #SqlResultSetMapping and #NamedNativeQuery (on database I use binary data that are represented by WKT String in entity). Everything works perfect if i use stateless beans with facade methods calling NamedNativeQueries and when I work ONLY with Geo objects.
My problem occurrs when I use Geo objects in related classes. I have entity class 'Location' related to Geo (according to tables in db), where i have joined column:
#JoinColumn(name = "GEOGUID")
#ManyToOne(fetch = FetchType.LAZY)
private Geo geoguid;
The geoguid object is always constructed by standard EnityManager method:
T find(java.lang.Class entityClass, java.lang.Object primaryKey).
Is there any possibility to annotate field/column in a way that EM will perform a defined native Query to get Geo?
Maybe, there is another way to convert single column data in my situation?
I was considering:
- implicit cast on db i.e. geometry->varchar,
- using #Converter implementing AttributeConverter
but from different reasons I could not use them.
Geo is class annotated with #Entity containing field coordinates as follows:
#Entity
#Table(name = "geo")
#XmlRootElement
#NamedQueries({....
#NamedNativeQueries({...
#SqlResultSetMappings({...
...
public class Geo implements Serializable
...//other fields
#Basic(optional = false)
#NotNull
#Column(name = "coordinates")
private String coordinates;
...
corresponding to binary data (of 'Point' type in MariaDB) in db table. I have mappings as described above and everything goes ok when I don't need fetching data by entity manager. I can create/update geo with my own nativequeries and the same with find, findAll and so on. All records in DB are correct. The problem appears when using joincolumn in other entity classes because geo instances are fetched then by EM standard find function and coordinates are not converted correctly (they are still strings but like '000000xccvbfrfrdd00000' instead of WKT 'POINT(10 10)').
The whole mapping is just 'astext(coords)' and i tried to put it into columndefinition, columnreferencedname etc. but no result.
How can I provide converted 'coordinates' field to other classes using ManyToOne or other relations? May i enforce EM to convert it somehow?

MongoDB and Morphia - Traditionnal id (Long) instead of ObjectId

Background:
My REST service project was started up by using Hibernate. I use id (Long) in domain class as part of the identifier in rest url, for example:
http://abc.com/customer-50, where 50 is the Long id.
The Hibernate Annotated class is as below:
public class Customer {
#Id
#GeneratedValue
private Long id;
}
Now I need to migrate our design to Mongodb. The natural choice is using Morphia, which is an entity framework.
Problem:
In Morphia, the id field is ObjectId
#Id private ObjectId id;
This will cause problem because:
1. It is not auto-increment, i.e. http://abc.com/customer-50, http://abc.com/customer-51, http://abc.com/customer-52.
Now it become http://abc.com/customer-4d1b4687a6d5437619000000
I will need to change all the reference classes from long to objectId.
Is it possible to keep the original design (which uses Long id, instead of ObjectId)?
Thanks!
Take a look at https://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/LongIdEntity.java https://github.com/mongodb/morphia/blob/master/morphia/src/test/java/org/mongodb/morphia/utils/LongIdEntity.java (link updated)
https://github.com/MorphiaOrg/morphia/blob/master/morphia/src/test/java/xyz/morphia/utils/LongIdEntity.java (updated again)

Hibernate not JPA compliant regarding #Access?

According to my JPA 2.0 book (and online documentation), I should be able to mix field and property access within a single entity or entity hierarchy. The annotation of #Access on the class specifies the default access. When placed on a field or property getter #Access can specify that the default should be overridden for this field.
#Entity
#Access(AccessType.FIELD)
Class Foo {
#Id
int id;
#Column(name = "myfield")
String myField;
#Column(name = "myProp")
#Access(AccessType.PROPERTY)
public int getMyProp () {
return 3;
}
public void setMyProp (int p) {
// do nothing
}
}
This class should result in a table with three columns. However it doesn't with Hibernate...the "myProp" column is missing from the table because apparently Hibernate takes its field vs property cue from the entity ID and runs with it...totally ignoring the JPA spec with regards to #Access.
Can anyone confirm this or did I make a stupid mistake somewhere?
I've seen similar (not the same but similar) issues like HHH-5004 so I wouldn't exclude that this might be a new one (the TCK doesn't seem exhaustive). But what version of Hibernate are you using? Did you try with the latest?
Based on the docs your code seems to be right. The #Access(AccessType.FIELD) annotation on top is unnecessary, because you annotated the field int id;
This tells hibernate to use field access. I tried a very similar example with annotations and xml config mixed. This leads to the same behaviour, so it's probably a bug in hibernate.
I tried with hibernate 3.5.3

Categories

Resources