I have an abstract base entity class with an id property.
I would like to have specific annotations applied to this property if the current database is H2:
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = id_generator")
#Column(name = "id")
private Long id;
And some other specific anotations applied to this property if the current database is MySQL:
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
#Column(name = "id")
private Long id;
The base class:
#MappedSuperclass
public abstract class AbstractEntity {
All entity classes extend the base class like:
#Entity
#Table(name = "user_account")
#SequenceGenerator(name = "id_generator", sequenceName = "sq_id_user")
public class User extends AbstractEntity {
Maybe I could leverage some conditional custom annotations ?
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Conditional(DbH2Condition.class)
public #interface DbH2 {
}
public class DbH2Condition implements Condition {
private static final String DB = "h2";
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("db") == null
|| context.getEnvironment().getProperty("db").equals(DB);
}
}
you can add annotations using external byte code manipulation framework, such as JavaAssist, during compile time. But it means that you need to know in compile time of course, which database you will use.
Related
In this code, I'm trying generate a composite key with that model class, but when I execute the post method throw an exception.
I'm using postgres as database. Then I need the hibernate generate the "RA" and Idprojeto automatically.
Model Id class
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#AllArgsConstructor
#NoArgsConstructor
class ProjetoId implements Serializable{
Long idProjeto;
Long ra;
}
Model Entity
#Entity
#Data
#IdClass(ProjetoId.class)
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "projeto", schema = "public")
public class Projeto {
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_projeto")
private Long idProjeto;
#Valid
#ConvertGroup(from = Default.class, to = ValidationGroups.ClienteId.class)
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_cliente")
private Cliente cliente;
#Valid
#NotNull
#ElementCollection
private List<ProdutoToProjeto> produtos = new ArrayList<>();
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ra")
private Long ra;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#Enumerated(EnumType.STRING)
#Column(name = "situacao")
private StatusProjeto situacao;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#Column(name = "data_criacao")
private LocalDate dataRegistro;
public void finalizar() {
if(naoPodeSerFinalizado()) {
throw new ProjetoException("Projeto não pode ser finalizado");
}
setSituacao(StatusProjeto.APROVADO);
}
public void cancelar() {
if(naoPodeSerFinalizado()) {
throw new ProjetoException("Projeto não pode ser cancelado");
}
setSituacao(StatusProjeto.RECUSADO);
}
public boolean podeSerFinalizado() {
return StatusProjeto.PEDENTE.equals(getSituacao());
}
public boolean naoPodeSerFinalizado() {
return !podeSerFinalizado();
}
}
Error
java.lang.IllegalArgumentException: Can not set java.lang.Long field com.ciasense.apiciasense.model.ProjetoId.ra to org.hibernate.id.IdentifierGeneratorHelper$2
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81) ~[na:na]
at java.base/java.lang.reflect.Field.set(Field.java:778) ~[na:na]
If you use DB SEQUENCE as the auto generation type only then composite key can work. Otherwise you have to write custom key generator and assign values to both the fields of your composite key before inserting.
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id_projeto")
private Long idProjeto;
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ra")
private Long ra;
If i understand, you want to do a key composite ?
If it is, you have two thing to do :
1- annoted your class with #IdClass as you do and put #Id on properties you want to link with your key and Equals and HashCode methods (your IDE generate it easily). (this thing is already done for you)
2- In your repository, you have to define your key also like normal key
example : (seems to not be done, it is the "ProjetoId" in the extends class)
#Repository
public interface ProjetoRepository extends JpaRepository<Projeto, ProjetoId> {
List<Projeto> findByDataRegistro(Date date);
}
you can also go to see baeldung which explain it with more details than me.
https://www.baeldung.com/jpa-composite-primary-keys
I need to create #EqualsAndHashCode by the inner object field. I have two entities WorkPlace and Account.
Shortcode:
***WorkPlace.java***
public class WorkPlace {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(optional = false)
#NotNull
private Account account;
.......................
}
***Account.java***
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...............
}
I want to create EqualsAndHashCode in WorkPlace.class using lombok like:
#EqualsAndHashCode(of = {"id", "account.id"})
When build a project - "account.id" ignored and created EqualsAndHashCode by id field.
Is it possible to do something like this using lombok?
You can add the following method and annotation in Workplace.java to get the desired result:
#EqualsAndHashcode.Include
public Long getAccountId() {
return account.getId();
}
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 want use custom class like ID
#javax.persistence.Embeddable
class ObjectID impements Serializable{
private Long value;
//setters, getters and other
}
Here is mapped class
#Entity
#IdClass(ObjectID.class)
public class Country implements Serializable
{
#Id
#AttributeOverride(name = "value", column = #Column(name = "id"))
#SequenceGenerator(name = "CountrySequenceGenerator",
sequenceName = "lab_country_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "CountryCountryGenerator")
private ObjectID value;
// setters, getters, fields
}
Doesn't work. Value wasn't generated. Any ideas, how can I generate ID for composite-id?
Id class has to be Serializable and implements hashCode and equals
Is there a way to specify distinct sequences for each table in Hibernate, if the ID is defined on a mapped superclass?
All entities in our application extend a superclass called DataObject like this:
#MappedSuperclass
public abstract class DataObject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private int id;
}
#Entity
#Table(name = "entity_a")
public class EntityA extends DataObject { ... }
#Entity
#Table(name = "entity_b")
public class EntityB extends DataObject { ... }
This causes all entities to use a shared sequence, the default hibernate_sequence.
What I would like to do is use a separate sequence for each entity, for example entity_a_sequence and entity_b_sequence in the example above. If the ID were specified on the subclasses then I could use the #SequenceGenerator annotation to specify a sequence for each entity, but in this case the ID is on the superclass. Given that ID is in the superclass, is there a way I can use a separate sequence for each entity — and if so, how?
(We are using PostgreSQL 8.3, in case that's relevant)
Have you tried doing it this way ?
#MappedSuperclass
public abstract class DataObject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idgen")
#Column(name = "id")
private int id;
}
#Entity
#SequenceGenerator(initialValue = 1, name = "idgen", sequenceName = "entityaseq")
#Table(name = "entity_a")
public class EntityA extends DataObject {
}
#Entity
#SequenceGenerator(initialValue = 1, name = "idgen", sequenceName = "entitybseq")
#Table(name = "entity_b")
public class EntityB extends DataObject {
}
I'm sorry I don't have the required environment to test it right now but I'll try it later.
We use this in the abstract superclass of all of our JPA entities:
#Id
#GeneratedValue(generator = "pooled")
#GenericGenerator(name = "pooled", strategy = "org.hibernate.id.enhanced.TableGenerator", parameters = {
#org.hibernate.annotations.Parameter(name = "value_column_name", value = "sequence_next_hi_value"),
#org.hibernate.annotations.Parameter(name = "prefer_entity_table_as_segment_value", value = "true"),
#org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled-lo"),
#org.hibernate.annotations.Parameter(name = "increment_size", value = "100")})
private Long id;
It's a bit verbose, but it allows setting the prefer_entity_table_as_segment_value which means you don't need to repeat the id field or the generator annotations in the subclasses.
I was not quite happy about the need to declare the sequence name on each class individually. I have checked the source code and came up with this solution:
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
// ...
#Id
#GeneratedValue(generator = "sequenceIdGenerator")
#GenericGenerator(
name = "sequenceIdGenerator",
strategy = "sequence",
parameters = #Parameter(
name = SequenceStyleGenerator.CONFIG_PREFER_SEQUENCE_PER_ENTITY,
value = "true"))
#Column(updatable = false, nullable = false)
protected Long id;
IHMO there is better way to do this:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
It works in my app.
TABLE generation stretergy uses separate db sequence for each table but it is little expensive operation