How should we handle id on Hibernate 6 on Sql server - java

We are migrating from Hibernate 5 to 6, and are having issues with the conversion of our id.
#MappedSuperclass
public abstract class Entity {
#Id
#Column(name = "ID")
#Convert(converter = UUIDConverter.class)
private UUID id = UUID.randomUUID();
}
The converter is set up like this:
#Converter
public class UUIDConverter implements AttributeConverter<UUID, String> {
#Override
public String convertToDatabaseColumn(UUID attribute) {
return attribute.toString();
}
#Override
public UUID convertToEntityAttribute(String dbData) {
return UUID.fromString(dbData);
}
}
We are using Sql Server, and the id is a uniqueidentifier in the database. If we save an object with id 8f935c03-0971-445e-9526-0ecbc743b470, this will be saved in the database as 035C938F-7109-5E44-9526-0ECBC743B470.
Any suggestion on how to solve this? What is a best-practice way to handle id`s? Some documentation say that we should not combine #Id with #Convert, but we have not found out what the alternative is.
We have tried converting to uppercase in the converter and we have tried using in IdClass.

JPA now comes with support for UUID built-in, so it should be as simple as:
#MappedSuperclass
public abstract class Entity {
#Id
#GeneratedValue
private UUID id;
}
Or, if you want some more control, check out the #UuidGenerator annotation in org.hibernate.annotations.
https://docs.jboss.org/hibernate/orm/6.2/javadocs/org/hibernate/annotations/UuidGenerator.html

Related

possible alternatives to #GeneratedValue with #EmbeddedId in JPA

My entity has a wrapped Identifier like this,
#Entity
public class Article {
#EmbeddedId
private ArticleId articleId;
....
}
#Embeddable
public class ArticleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
in my architecture, multiple application instances(it all same.) are connected to the same data source
so #GeneratedValue(strategy = GenerationType.IDENTITY)seems good
because even if instances A and B try to create Accountat the same time, Its Id is guaranteed by the database.
problem is that #GeneratedValue is only able to use with #Id annotation (#Id is not available for EmbeddedId)
PersistenceUnitUtil.getIdentifier(Object entity) could be a alternative? like this,
ArticleId articleId = ArticleRepository.nextIdentity();
I am not sure that whether it causes the race condition.
Could PersistenceUnitUtil.getIdentifier(Object entity) guarantee the unique id across the different application instance(JVM)? I don't think so.
In this situation, What alternative is possible?
One solution could be to use an #IdClass to get rid of the nested property and be able to generate the identifier (since nested properties are "assigned" and cannot be generated, so the call of PersistenceUnitUtil.getIdentifier(Object entity) would not help here). See e.g. here for a complete guide (also linked in the linked answer by #SternK from the comment)
An #IdClass could look like this:
public class ArticleId implements Serializable {
private Long id;
}
An entity could use it:
#Entity
#IdClass(ArticleId.class) // specified dependency
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
// expose "typed" id:
public ArticleId getId() {
return new ArticleId(id);
}
}
Spring-Data #Repositorys also work with corresponding #IdClass objects, e.g.:
#Repository
public interface UserEntityRepository extends JpaRepository<Article, ArticleId> {
}
// would offer e.g. this method:
repository.findById(new ArticleId(123L));

Spring Data JPA mapping one-to-one relationship returns null value

I am making a Spring Boot backend, and I have the following problem. When I get a Software from VersionableFileRepository and call the getSystem function on that I get the actual System within the relationship. But when I get a Documentation from VersionableFileRepository its getSystem function returns null. I handle the Software and Documentation in the same way, and all instance of these have a System.
Illustrated with code:
versionableFileRepository.findById(fileId).get().getSystem() returns a valid System when fileId identify a Software and returns null when a Documentation
What's wrong? Did I mess something up in the implementation?
I have the following classes:
#Entity
public class System {
#Id
#GeneratedValue
private long id;
private String name;
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
#JoinColumn(name = "software_id", referencedColumnName = "id")
private Software software;
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
#JoinColumn(name = "documentation_id", referencedColumnName = "id")
private Documentation documentation;
//other fields, getters and setters...
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class VersionableFile {
#Id
#GeneratedValue
private long id;
#OneToMany(mappedBy = "file", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<FileVersion> versions = new ArrayList<>();
public abstract System getSystem();
public abstract void setSystem(System system);
//getters and setters...
}
#Entity
public class Software extends VersionableFile {
#OneToOne(mappedBy = "software")
#JsonIgnore
private System system;
#Override
public System getSystem() {
return system;
}
#Override
public void setSystem(System system) {
this.system = system;
}
}
#Entity
public class Documentation extends VersionableFile {
#OneToOne(mappedBy = "documentation")
#JsonIgnore
private System system;
#Override
public System getSystem() {
return system;
}
#Override
public void setSystem(System system) {
this.system = system;
}
}
#Repository
public interface VersionableFileRepository extends CrudRepository<VersionableFile, Long> {
}
Database:
Everything looks good in the database, this is the system table:
And the corresponding objects can be found in the other two tables (software and documentation). Furthermore the appropriate constraints are also defined.
I think this is a JPA issue, because when I get a System object from SystemRepository (not mentioned here) it has the right software and documentation fields.
Thank you in advance for your help!
Have already commented but looking better I think I found something major here.
Proposal 1
Your Entities structure seems good to me. However you have a major Issue with your java code to retrieve those entities back from database.
versionableFileRepository.findById(fileId).get().getSystem()
fileId as well as documentId are plain Long numbers. How would JPA know if you want to retrieve a Software or a Documentation? This will not work. As you have constructed it, it will have separate tables Documentation and Software and each one of those will have a column Id as primary key.
Make it easier for JPA by using specific repositories
#Repository
public interface SoftwareRepository extends CrudRepository<Software, Long> {
}
Then to retrieve software just use softwareRepository.findById(id).get().getSystem()
And
#Repository
public interface DocumentationRepository extends CrudRepository<Documentation, Long> {
}
Then to retrieve documentation just use documentationRepository.findById(id).get().getSystem()
Proposal 2
If you wish to go along the way you are going then I would consider that the error is specifically on your ids that are generated. You want different tables in your case Documentation and Software to have distinct Ids. Then JPA could distinct from the Id what entity you have.
To achieve that you have to change the strategy of generating Ids
public abstract class VersionableFile {
#Id
#GeneratedValue( strategy = GenerationType.TABLE)
private long id;
....

Java persistence mapped superclass with optional properties

I'm using the javax.persistence package to map my Java classes.
I have entities like these:
public class UserEntity extends IdEntity {
}
which extends a mapped superclass named IdEntity:
#MappedSuperclass
public class IdEntity extends VersionEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// Getters and setters below...
}
The IdEntity super class extends another mapped super class named VersionEntity to make all entities inherit version properties:
#MappedSuperclass
public abstract class VersionEntity {
#Version
private Integer version;
// Getters and setters below...
}
Why?
Because now I can make generic queries on the IdEntity class for all entities, and it will look like this: (example)
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);
Now to the problem.
Some of my entities will have timestamps like created_at and deleted_at. But not all entities.
I could provide these properties in my entity classes like this:
public class UserEntity extends IdEntity {
#Basic(optional = false)
#Column(name = "updated_at")
#Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
}
But as I have a lot of entities, this will make me put a lot of redundant code in all entities that should have timestamps. I wish there was some way I could make the relevant classes inherit these fields in some way.
One possible solution is to create a parallell IdEntity superclass, maybe named IdAndTimeStampEntity and make those entities that should have timestamps inherit from this new superclass instead, but hey that's not fair to my colleague-developers because now they have to know which super class to choose from when writing generic queries:
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<???> criteria = builder.createQuery(???); // Hmm which entity should I choose IdEntity or IdAndTimeStampEntity ?? *Annoyed*
And the generic entity queries become not so generic..
My question: How can I make all of my entities inherit id and
version fields, but only a sub part of all entities inherit
timestamp fields, but keep my queries to a single type of entities?
Update #1
Question from Bolzano: "can you add the code which you specify the path(holds table info) for entities ?"
Here is a working example of querying a UserEntity which is a IdEntity
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);
Root<IdEntity> from = criteria.from(IdEntity.class);
criteria.select(from);
Path<Integer> idPath = from.get(UserEntity_.id); //generated meta model
criteria.where(builder.in(idPath).value(id));
TypedQuery<IdEntity> query = JPA.em().createQuery(criteria);
return query.getSingleResult();
I would pick a solution that didn't enforce a class-based object model like you've outlined. What happens when you don't need optimistic concurrency checking and no timestamps, or timestamps but no OCC, or the next semi-common piece of functionality you want to add? The permutations will become unmanageable.
I would add these common interactions as interfaces, and I would enhance your reusable find by id with generics to return the actual class you care about to the caller instead of the base superclass.
Note: I wrote this code in Stack Overflow. It may need some tweaking to compile.
#MappedSuperclass
public abstract class Persistable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// getter/setter
}
public interface Versioned {
Integer getVersion();
}
public interface Timestamped {
Date getCreated();
Date getLastUpdated();
}
#Embeddable
public class TimestampedEntity {
#Column(name = "create_date")
#Temporal
private Date created;
#Column
#Temporal
private Date lastUpdated;
// getters/setters
}
#Entity
public class UserEntity extends Persistable implements Versioned, Timestamped {
#Version
private Integer version;
#Embedded
private TimestampedEntity timestamps;
/*
* interface-defined getters. getTimestamps() doesn't need to
* be exposed separately.
*/
}
public class <CriteriaHelperUtil> {
public <T extends Persistable> T getEntity(Class<T> clazz, Integer id, SingularAttribute idField) {
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(clazz);
Root<T> from = criteria.from(clazz);
criteria.select(from);
Path<Integer> idPath = from.get(idField);
criteria.where(builder.in(idPath).value(id));
TypedQuery<T> query = JPA.em().createQuery(criteria);
return query.getSingleResult();
}
}
Basic Usage:
private UserEntity ue = CriteriaHelperUtil.getEntity(UserEntity.class, 1, UserEntity_.id);
ue.getId();
ue.getVersion();
ue.getCreated();
// FooEntity implements Persistable, Timestamped
private FooEntity fe = CriteriaHelperUtil.getEntity(FooEntity.class, 10, FooEntity_.id);
fe.getId();
fe.getCreated();
fe.getVersion(); // Compile Error!
#MappedSuperclass
public class IdEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Version
private Integer version;
}
#MappedSuperclass
public class IdAndTimeStampEntity extends IdEntity{
Date created;
}
#Entity
public class UserEntity extends IdAndTimeStampEntity{
String name;
}
#Entity
public class FooEntity extends IdEntity{...
Pros of this solution:
In simple and clear way uses OOP without need to embed duplicate code implementing intefaces in every subclass. (Every class is also interface)
Optimistic locking version column is mostly used approach. And should be part of base class. Except read only entities like codetables.
Usage:
public <T extends IdEntity> T persist(T entity) {
if (entity instanceof IdAndTimeStampEntity) {
((IdAndTimeStampEntity) entity).setCreated(new Date());
}
if (!em.contains(entity) && entity.getId() != null) {
return em.merge(entity);
} else {
em.persist(entity);
return entity;
}
}
I wish there was some way I could make the relevant classes inherit these fields in some way.
You could make a custom annotation #Timed and use an annotation processor to add the timestamp field and annotations, either by using a bytecode manipulation framework or creating a delegating subclass. Or, for example if you use Lombok, create a Lombok annotation.
That way, your team members only have to remember to use the #Timed annotation when you have entities with timestamps. Whether you like such approach or not is up to you.

Hibernate jpa does honour AttributeConveter

I have defined an attribute converter like this:
#Converter(autoApply = true)
public class MyConverter implements AttributeConverter<MyType, String> {
#Override
#Nullable
public String convertToDatabaseColumn(#Nullable final MyType attribute) {
LOG.log(Level.INFO, "Converting '{'{0}'}' to DB column", attribute);
return attribute != null ? map(attribute) : null;
}
#Override
#Nullable
public MyType convertToEntityAttribute(#Nullable final String dbData) {
LOG.log(Level.INFO, "Converting '{'{0}'}' to type", dbData);
return dbData != null ? map(dbData) : null;
}
}
Then in my entity class:
#Entity
public class MyEntity implements Serializable {
#Id
private Long id;
private MyType myData;
}
According to jpa2.1 specs, i do not have to annotate the attribute field with #Convert if i have specified autoApply on the converter.
Nonetheless, even if i do not specify the autoApply on the converter and specify the following:
#Entity
public class MyEntity implements Serializable {
#Id
private Long id;
#Convert(converter = MyConverter.class)
private MyType myData;
}
Hibernate still does not consider this converter.
What could i be doing wrong?
I have deleted the table and regenerated it, but nothing does help.
I have tried hibernate versions from 4.3.4 - 4.3.8 with no success, n wildfly 8.1
As aside note, My converter is declared in an entity-jar, which is then included in ejb-jar as a dependency.
Well.
After several hours, the solution seems to be simple.
My mistake was that i declared classes in the ejb-jars persistence.xml, instead of specifying the jar-file element. Therefore, the jpa hibernate annotation engine had no idea of my entity-jar, and could not scan it for the Converter annotation

Is there a way (e.g. an Eclipse plugin) to automatically generate a DTO from an Entity (JPA)?

I would like a plain forward DTO generation tool that would either
Generate it on the fly (e.g. cglib - create the class and DTO object on the fly)
Or an Eclipse plugin that will take the Entity and generate a DTO (user will specify which tree graph to include, and for non included, will include foreign keys instead of related entities etc)
E.g. take something like this
#Entity
#Table(name="my_entity")
public class MyEntity {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private RelatedEntity related;
public RelatedEntity getRelated(){
return related;
}
...
And generate something like this :
#Entity
#Table(name="my_entity")
public class MyEntity imlpements MyEntityDTO {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private RelatedEntity related;
//overrides MyEntity interface, it's allowed to narrow return type
public RelatedEntity getRelated(){
return related;
}
...
//implements MYEntityDTO respective interfaces
public Long getRelatedId(){return related.getId();}
And DTO interface(s):
public interface MyEntityDTO {
public String getId();
public String getName();
public Long getRelatedId();
public RelatedEntityDTO getRelated(); //RelatedEntity implements RelatedEntityDTO
...
}
public interface RelatedEntityDTO {
...
}
If we don't want to include children in the graph, remove it from the DTO interface:
public interface MyEntityDTO {
public String getId();
public String getName();
public Long getRelatedId();
...
I'm sure there is some eclipse plugn for it and if not, I challange someone to write one, or explain why what I want is not helpful (and provide an alternative suggestion)
Probably Hibernate Tools should be doing this: http://hibernate.org/subprojects/tools.html
Telosys Tools can generate both : JPA entity and DTO
Let's have a look at this tutorial https://sites.google.com/site/telosystutorial/springmvc-jpa-springdatajpa
it generates a full Spring MVC CRUD application with JPA
Architecture : https://sites.google.com/site/telosystutorial/springmvc-jpa-springdatajpa/presentation/architecture
The mapper Entity/DTO is also generated (it uses "org.modelmapper" )
The templates are customizable
Try to look at:
https://github.com/nikelin/spring-data-generation-kit
But it's only suitable for you if your project is under the
Maven control.

Categories