I'm trying to use objects as #Id but Hibernate doesn't seem to convert the object to the corresponding primitive type stated using an AttributeConverter.
#Entity
#Table(name = "ris_exam")
public class Exam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long examId;
}
public class ExamId {
private Long id;
public ExamId(final Long id) {
super();
this.id = id;
}
public Long getId() {
return this.id;
}
}
#Converter(autoApply = true)
public class ExamIdCodeAttributeConverter implements AttributeConverter<ExamId, Long> {
#Override
public Long convertToDatabaseColumn(ExamId arg0) {
return arg0.getId();
}
#Override
public ExamId convertToEntityAttribute(Long arg0) {
return new ExamId(arg0);
}
}
Hibernate does this query when creating the table
create table ris_exam (id varbinary(255) identity not null, ...)
If you want to play around with wrapping of the basic column then you must take into consideration that most likely you will not be able to use the #GeneratedValue annotation on your id (the same goes for using the #EmbeddedId which is more suitable than the converter in my opinion anyway).
If you need to anyway then in my opinion you could try the #IdClass feature but remember that the ExamId class would only be used while specifiying the queries / retrieving specific entity:
#Entity
#Table(name = "ris_exam")
#IdClass(ExamId.class)
public class Exam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long examId;
}
in DAO:
ExamId id = new ExamId(1L);
session.get(Exam.class, id);
when retrieved, the entities would contain plain long types.
Just an option for you to consider..
Related
Suppose that I have a simple Hibernate entity with auto-incremented id.
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
private String name;
}
Is it possible to declare id as a type-safe variable? I could apply #EmbeddedId like this.
#Entity
#Table(name = "product")
public class Product {
#EmbeddedId
private ProductId id;
private String name;
#Embeddable
public static class ProductId implements Serializable {
#GeneratedValue(strategy = IDENTITY)
private Long id;
public Long getId() {
return id;
}
}
}
It works with client-generated IDs, but not with database-generated ones.
Has anyone solved similar problem? What are the possible approaches?
First, you need to annotate ProductId class with #Embeddable like this:
#Embeddable
public static class ProductId implements Serializable {
private Long id;
private String name;
}
And, when you save entity, you need to create an instance of ProductId with unique parameters (in your case it is "name" field) as well.
For more information, I suggest you to have a look at here
I have object with #Id (primary key) and this is some business UIID field and I want another Long technical Id for some reasons but when saving the object I get null from getObjectId field:
#Id
private String id;
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "object_id")
private Long objectId;
public Long getObjectId() {
return objectId;
}
public void setObjectId(Long objectId) {
this.objectId = objectId;
}
I have this storage class:
interface MyObjectStorage extends JpaRepository<MyObject, String>
And this is how I save it:
final MyObject saved = storage.saveAndFlush(toSave);
saved.objectId is null here..
And in my MySQL db the objectId field is marked not null auto increment..
PS. And I don't want this objectId field to be used by JPA when findById is executed.
Hibernate/JPA isn't able to automatically create a value for your non-id-properties. The #GeneratedValue annotation is only used in conjunction with #Id to create auto-numbers.
The solution (or work-around) suggested in this forum is to create a separate entity with a generated Id, something like this:
#Entity
public class GeneralSequenceNumber {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "object_id")
private Long objectId;
}
#Entity
public class MyEntity {
#Id ..
private Long id;
#OneToOne(...)
private GeneralSequnceNumber myVal;
}
try the following code
#Id must be a type of Integer or Long
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "object_id")
private Long objectId;
private String id;
public Long getObjectId() {
return objectId;
}
public void setObjectId(Long objectId) {
this.objectId = objectId;
}
interface MyObjectStorage extends JpaRepository<MyObject, Long>
This question already has answers here:
Hibernate : How override an attribute from mapped super class
(2 answers)
Closed 5 years ago.
I have a problem for #Id in JPA that is described as follow?
There is a generic class as follow?
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
private static final long serialVersionUID = 4295229462159851306L;
private T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
There is another class that extends from it as follow?
#Entity
#Table(name = "DOC_CHANGE_CODE" )
public class ChangeCode extends BaseEntity<Long> {
#Id
#GeneratedValue(generator = "sequence_db", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "sequence_db", sequenceName = "SEQ_DOC_CHANGE_CODE", allocationSize = 1)
public Long getId () {
return super.getId();
}
}
Because any sub class has its own sequence, I must specific any subclass #Id, because of that I override the its getter and put some annotations in top of that. Unfortunately it does not work correctly.
How do I fix problem and get my goal?
Try this:
#Entity
#Table(name = "DOC_CHANGE_CODE" )
#SequenceGenerator(name = "sequence_db", sequenceName = "SEQ_DOC_CHANGE_CODE", allocationSize = 1)
#AttributeOverride(name = "id", column = #Column(name = "ID"))
public class ChangeCode extends BaseEntity<Long> {
#Override
#Id
#GeneratedValue(generator = "sequence_db", strategy = GenerationType.SEQUENCE)
public Long getId() {
return id;
}
}
It's not possible to override the #Id from a base class.
The only way to do it is to provide your own custom IentifierGenerator and provide a different logic based on the subclass (e.g. using a sequence name based on the subclass name).
That's why adding the #Id attribute in the #MappedSuperclass only makes sense for the assigned generator or for IDENTITY.
I am working on converting an existing project over to use Hibernate. I have a class like this:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "group_id_user")
private Long groupId;
#Column(name = "name")
private String name;
...
// getters and setters....
}
and a class like this:
#Entity
#Table(name = "group")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private Long groupId;
#Column(name="group_name")
private String groupName;
...
// getters and setters....
}
The column named "group_id_user" in the user table is supposed to be a foreign key to the column named "group_id" in the group table.
Is it okay or "correct" to have the classes structured as shown above or should they be structured as shown below to make sure that the foreign key exists in the Database?
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#ManyToOne
#JoinColumn(name = "group_id_user")
private Group group;
#Column(name = "name")
private String name;
...
// getters and setters....
}
and
#Entity
#Table(name = "group")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private Long groupId;
#Column(name="group_name")
private String groupName;
...
// getters and setters....
}
I have tried using both formats but have had issues both ways. When I use the first format I have issues with the HQL syntax for joins while creating queries. When I try the second format I have issues with fetching just a User from the database without a Group, or adding a new User from a json object the has a groupId instead of a Group object. So before I spend anymore time switching back and forth between the two formats I want to know for sure which way should I be using the annotations to best fit industry standard?
I would try something like this if you could change the name of the columns too:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) //This means you will need the database to generate the ids, if you are using Oracle this won't work. You would need to use SEQUENCE.
private Long id;
#ManyToOne
#JoinColumn(name = "group_id") //There will be no need to specify the join column if you use group_id.
private Group group;
#Column(name = "name")
private String name;
...
// getters and setters....
}
#Entity
#Table(name = "groups")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="group_name")
private String groupName;
...
// getters and setters....
}
Also if you can I would change the name of the tables to plural.
Also I use something that helps me a lot. I have a super class called "Identifiable" which just has the id and it looks like this:
#MappedSuperclass
public class Identifiable implements Serializable {
private static final long serialVersionUID = -9027542469937539859L;
#Id
#Column(name = "ID")
#GeneratedValue
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Identifiable other = (Identifiable) obj;
if (id == null) {
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
With that you can extend all your classes with ids easily like:
#Entity
#Table(name = "users")
public class User extends Identifiable {
private static final long serialVersionUID = -90275424699375956859L;
#ManyToOne
#JoinColumn(name = "group_id") //There will be no need to specify the join column if you use group_id.
private Group group;
#Column(name = "name")
private String name;
...
// getters and setters....
}
However if you cannot change the names, let us know the issues you are having with the traces that are throwing and we might be able to help.
Thanks!
When im using table-per-subclass strategy my base (parent class) contains compound primary.
#Column(nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
#SequenceGenerator(name = "XXX", sequenceName = "XXX", allocationSize = 1)
private Long systemId;
#Id
#Column(nullable = false)
private Long version;
So, all entities that extend this (as i got it) inherit ids and its annotated properties.
The problem, is that when i create a new Parent entity, the primary is being created from sequence, thats ok. But when i create subclass instance it increments my parent id i dont need (creates a new primary from seq.), `coz i use table-per-subclass and primaries gottta be the same.
Question: how to supress id generation in Subclasses?
#Entity
#PrimaryKeyJoinColumns(
{
#PrimaryKeyJoinColumn(name = "systemId"),
#PrimaryKeyJoinColumn(name = "version")
}
)
public class SUb extends Parent {
public SUb (Parent t) {
super(t);
}
public SUb () {
}
...
... no ids...
When the Subclass is created it will issue two inserts:
one in the base class
one in the sub class
The subclass doesn't inherit the #Ids, but instead it uses the:
systemId
version
as FK to the associated columns of the Base table.
So while the base class has a primary key composed of those two columns:
a sequence generated systemId
a manually assigned version
The subclass should have:
a sequnceId column with a FK to the base sequnceId column
a version column with a FK to the base version column
So the sequence shouldn't be called twice while inserting a sub class entity.
Can you confirm that the database tables are following this design?
I couldn't manage to reproduce your problem. Can you be more specific on what is your join strategy? Is it InheritanceType.TABLE_PER_CLASS or InheritanceType.JOINED? Nonetheless I managed to prepare complete example with TABLE-PER-CLASS join strategy which does increment the sequence only once when you persist SubDepartment. Maybe it helps.
#Entity
#IdClass(CompoundPK.class)
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
#SequenceGenerator(name = "XXX", sequenceName = "XXX", allocationSize = 1)
#Id
private Long id;
#Id
private Long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
#Entity
public class SubDepartment extends Department {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class CompoundPK implements Serializable {
private Long id;
private Long version;
public CompoundPK() {
}
public CompoundPK(Long id, Long version) {
this.id = id;
this.version = version;
}
public Long getId() {
return id;
}
public Long getVersion() {
return version;
}
// hashCode & equals
}