Modifying the generated ID, UUID, removing dashes - java

Due to compatibility reasons with existing systems, I have to store the UUID type as varchar without dashes. What would be the simplest solution to do this?
I have created a sample class below to demonstrate the question. When the instance of the class is persisted, the UUID is in the DB fine, just with the dashes in the tab1_id column e.g. 583cfe1a-d671-47f9-9cd5-3f1e8f717856. What I would need is: 583cfe1ad67147f99cd53f1e8f717856. So the question is how I could change the generated id after its generation.
I am using spring-boot and hibernate as JPA implementation. Any constructive response is appreciated. Thanks in advance.
#Entity
#Table(name = "tab1")
#XmlRootElement
public class Tab1 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Column(name = "tab1_id", updatable = false, nullable = false, columnDefinition = "uuid")
#Type(type = "org.hibernate.type.UUIDCharType")
private UUID tab1Id;
#Column(name = "name")
private String name;
....

You'll have to implement a custom ID generator and your entity will have an ID of String as it's no longer going to be a valid UUID.
Something like this will work
#Id
#GenericGenerator(name = "mygen", strategy = "com.abc.generator.IdGenerator")
#GeneratedValue(generator = "mygen")
private String id;
Your generator will look like this:
package com.abc.generator;
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
public class IdGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o)
throws HibernateException {
return UUID.randomUUID().toString().replace("-", "");
}
}

Do you need it persisted on database? If that is the case I guess it wouldn't be possible. But if you want to retrieve it without the dashes just do the getUUID method of your entity class return a string without the dashes.

Related

JPA 2 Hibernate mapping with composite key in primary key using #IdClass with 3 tier structure

This question is very similar to: JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, #EmbeddedId composite PK-FK)?
Actually my only (from meaningful that I spotted) difference is that I use #IdClass and that I most probably won't be able to switch to a different provider than hibernate.
but anyway here is the code (removed parts that where unimportant):
PermissionContextType.java:
#Entity
#IdClass(PermissionContextTypePk.class)
public class PermissionContextType{
#Id
private String id;
#Id
#JoinColumn (name = "PROJECT", referencedColumnName = "ID")
#ManyToOne ()
private Project project;
public static class PermissionContextTypePk implements Serializable{
public String project;
public String id;
// ... eq and hashCode here ...
}
}
PermissionContext.java:
#Entity
#IdClass(PermissionContextPk.class)
public class PermissionContext{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContextType permissionContextType;
public static class PermissionContextPk implements Serializable{
public String id;
public PermissionContextTypePk permissionContextType;
// ... eq and hashCode here ...
}
}
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE"),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
public String id;
public PermissionContextPk permissionContext;
// ... eq and hashCode here ...
}
}
and what I get is:
org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
Caused by: org.hibernate.AssertionFailure: org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
does anybody know if this is a hibernate bug and I should post it on their issue tracking system (and pray that I would be able to update to given hibernate version) or is there something fundamentally wrong with my way of binding the entities?
I've checked it with the hibernate implementation on EAP 6.1 (4.2.0) as well as on wildfly (don't really know which one.)
Ok, so this is what I found so far :
Thanks fr my friend : https://hibernate.atlassian.net/browse/HHH-5764 which most probably is the reason for this behaviour.
And I found a workaround :
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
// for the next 3 fields there are no public acessors, so the public API of the class was not changed !
#Id
#Column(name = "PROJECT")
private String projectId;
#Id
#Column(name = "PERMISSIONCONTEXTTYPE")
private String permissionContextTypeId;
#Id
#Column(name = "PERMISSIONCONTEXT")
private String permissionContextId;
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID", updatable = false, insertable = false)
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
// previously they where private as well, but removed public constructor for the sake of simplicity of the question - so no changes where necesary in public API of the class !
private String id;
private String projectId;
private String permissionContextTypeId;
private String permissionContextId;
public PermissionPk () {}
public PermissionPk (String aId, PermissionContextPk aPermissionContext) {
this.id = aId;
permissionContextId = aPermissionContext.id;
permissionContextTypeId = aPermissionContext.permissionContextType.id;
projectId = aPermissionContext.permissionContextType.project;
}
... eq and hashCode here ...
}
}
The good thing about this workaround is that it does not change the public API of the class in any way
(the only change was that I needed to make fields in Pk's of context and contexttype visible to the PermissionPk - they where private before with only a public constructor [but again simplified for the question]), nor did it change the jpql queries, and at the same time workaround is scalable (to any tier amount - as long as every even pk does not contain another pk), so if the bug will be resolved it will be easy to remove the workaround.
I would still gladly accept any comments on either my workaround or the question in itself.
Today I found another workaround :)
You can omit #IdClass entirely and use hibernate specific ability to create composite keys on the fly as apparently it is not affected by this bug.
The drawback here is that:
it is entirely Hibernate specific not covered by JPA at all.
you cannot do em.find(ClassName.class,new ClassPk(args...)) as there is no ClassPk at all.
But if you could use anything else than hibernate you could just as well use something without this bug - so probably 1 is not a problem really. and there is a possibility that you don't really need the em.find for this entity (or can live with creating it thru session or jpql query).

JPA with #ManyToOne

I have two classes like this
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
#Entity
#Table(name = "commitment_type_value")
public class CommittmentTypeValue extends Model{
#Id
#Column(name = "id", nullable = false)
public Long id;
#Column(name = "value", nullable = true)
public String type;
#ManyToOne
#JoinColumn(name="commitment_type_id")
public CommitementType commitmentType;
public CommittmentTypeValue(){
}
}
-------------
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
/**
*
* #author hatem
*/
#Entity
#Table(name = "commitment_type")
public class CommitementType extends Model{
#Id
#Column(name = "id", nullable = false)
public Long id;
#Column(name = "type", nullable = true)
public String type;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="commitmentType")
public List<CommittmentTypeValue> commitmentTypeValues;
public CommitementType(){
}
}
when I execute my app, this problem appears :
A JPA error occurred (Unable to build EntityManagerFactory): A Foreign
key refering models.CommitementType from models.CommittmentTypeValue
has the wrong number of column. should be 2
Please, can Any one tell me what 's wrong ?
Please check your foreign key column name it should match the exactly with the name of column.
EDIT
If your problem is still unsolved then please check if your persistance.xml has
<property name="generateDdl" value="true" />
and if it already have then check if you are getting any error in generation of table.
If yes then clear the data in table
or
add drop-and-create-tables option in config file
or
change your code as follows
#ManyToOne(optional=true)
#JoinColumn(name="commitment_type_id", nullable = true)
public CommitementType commitmentType;
Because you might have old data in table which might be stopping the creation of new table.
Reference column name is missing in your many to one join in class CommittmentTypeValue
#ManyToOne
#JoinColumn(name="commitment_type_id" referencedColumnName="id" )
public CommitementType commitmentType;
Also specify target entity
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, targetEntity=CommittmentTypeValue.class, mappedBy="commitmentType")
public List<CommittmentTypeValue> commitmentTypeValues;
The error sounds like you Id in CommitementType is composite, so your foreign key must contain two columns.
Include the code for the CommitementType.

JPA: Override Auto generated ID [duplicate]

This question already has answers here:
Bypass GeneratedValue in Hibernate (merge data not in db?)
(8 answers)
Closed 5 years ago.
I have following definition in Employee class
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
Now I want to import existing employees with existing employee IDs. Even if I set the employee ID before saving, the assigned ID is ignored and auto incremented ID is stored. How do we override this?
I had similar problem for composite key which has been explained here
I wrote my own generator to solve the issue.
public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
and use it like this: (replace the package name)
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#Column(unique = true, nullable = false)
protected Integer id;
You cannot override it. First of all JPA do not provide way to do it. Second problem is that GenerationType.AUTO can produce such a column type that will not accept user assigned value via SQL statement.
Perform import of data manually and do not try to use application itself as an import tool.
you must override getEmployeeId() method and use Annotation on this method
in this case JPA uses the getter methods for all fields in class and you must move other Annotaions on getter methods
In this case, it might be best to have a #MappedSuperclass
#MappedSuperclass
public AbstractEmployee {
// declare all properties, getters, setters except for the id
}
#Entity
#Table(name="EMPLOYEE")
public class Employee extends AbstractEmployee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
...
}
#Entity
#Table(name="EMPLOYEE")
public class EmployeeWithAssignedId extends AbstractEmployee {
#Id
#Column(name = "employee_id")
private Integer employeeId;
...
}
So when you want to assign the ID manually, use the second class. I have not tested this, but I believe it should work for your situation. I don't believe there's any rule in JPA that says two classes can't have the same table name :)

[PersistenceException: Error getting sequence nextval]

i am getting this error while trying to save data into model in db.
#Entity
public class User extends Model {
#Required
public String name;
#Email
public String email;
#Required #MaxLength(value=10)
public String username;
#Required #MinLength(value=4)
public String password;
#Id
public int id;
}
this is my Class.
this is the error while i am trying to save the model into db.
i will appreciate any effort for help! many thanks.
EDIT: table structure is here
I think with ebean you have to physically name and annotate your id. You may also have to tell it the name of the backing sequencer as well (I dont remember). This shows how to do it.
This worked for me:
#Entity
#Table(name = "table", schema = "schema")
public class Bean extends Model{
#Id
#Column(name = "idcolumn")
#SequenceGenerator(name="gen", sequenceName="schema.table_idcolumn_seq",allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "gen")
private int id;
}
When using the SequenceGenerator, please mind this bug in Hibernate: https://hibernate.atlassian.net/browse/HHH-7232
It forces you to write the schema directly into the sequenceName instead of using the schema field in the SequenceGenerator annotation.
This worked for me on class annotation:
#SequenceGenerator(name = "SEQUENCE_NAME", sequenceName = "PST_BUSINESS.S_BUSINESS_DOMAIN")
#Entity
#Table(name = "TB_BUSINESS_DOMAIN", schema = "PST_BUSINESS")
public class PstBusinessDomain extends PstAbstractBaseMappedEntity {
As Leo said, this strategy works for annotation in the field and also in the class.

Can #TableGenerator save the last used id in the table instead of the next available?

I need to connect to a vendor db and insert customer data. The sequence table used to generate new customer ids stores the last used id (not the next available). I couldn't find anything in the jpa or hibernate docs that would indicate a way to tell hibernate to treat the id in the seq table as last used instead of next available (when using #TableGenerator).
Do I need to write a custom generator that will behave essentially the same as #TableGenerator with the only difference being the way the value in the sequence table is treated?
My Customer entity is defined as follows:
#Entity
public class Customer {
#Id
#TableGenerator(name = "cust_gen", table = "SEQUENCE", pkColumnName = "target",
pkColumnValue = "customer", valueColumnName = "id", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.TABLE, generator = "pat_gen")
public long getCustomer_id() {
return customer_id;
}
public void setCustomer_id(Long id) {
this.customer_id = id;
}
...
}
Thanks!
I had the same problem. Fixed it this way:
Use Hibernate org.hibernate.annotations.GenericGenerator instead of persistance TableGenerator like this:
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.annotations.GenericGenerator;
#Entity
#Table(name = "name")
public class Name implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "names")
#GenericGenerator(name = "names", strategy = "com.ourpackage.SybaseTableGenerator", parameters = {
#Parameter(name = "table_name", value = "new_key_numbers"),
#Parameter(name = "value_column_name", value = "key_number"),
#Parameter(name = "segment_column_name", value = "name"),
#Parameter(name = "segment_value", value = "names_key") })
#Column(name = "names_id")
private Long id;
And create your own generator (I used name com.ourpackage.SybaseTableGenerator):
import java.io.Serializable;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.enhanced.TableGenerator;
#SuppressWarnings("UnusedDeclaration")
public class SybaseTableGenerator extends TableGenerator {
#Override
public synchronized Serializable generate(SessionImplementor session, Object obj) {
return (Long) super.generate(session, obj) + 1;
}
}
A little bit tricky but it works ;)

Categories

Resources