I'm using hibernate in my spring boot application
my domain model is like this
#Entity
#Table(name = "skill")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "skill")
#Audited
public class Skill implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
}
The increment size of sequence is 50 and is working properly
but when I add Envers Audited annotation I see this error
conflicting values for 'increment size'. Found [50] and [1]
How can I resolve this conflict?
This doesn't sound like an Envers problem but a general mapping problem.
When you add an #Audited annotation, that simply informs Envers that it should inspect that particular entity mapping during Hibernate bootstrap and create the necessary audit objects to store the entity state during each transaction.
The generated Envers objects use their own sequence generators and primary key. The user defined generation strategy, sequences, etc are all ignored in the Envers object because the associated column is meant to just be a copy/pass-thru value, nothing special.
In other words, the Envers table would have a PK defined that mirrors this POJO:
#Embeddable
public class EnversSkillId implements Serializable {
#Column(name = "REV", nullable = false, updatable = false)
private Integer rev;
#Column(name = "id", nullable = false, updatable = false)
private Long id;
}
When Envers generates the audit record, it automatically uses its internal sequence generator to get the next value and assign it to EnversSkillId#rev and copies your entity's id value directly into the EnversSkillId#id property.
So as mentioned in the comments, your problem is very unlikely related to Envers.
Related
I have such a case where I need to have internally many-to-one using hibernate proxies and only id externally, here using MapsId. The issue appears when I try to save something, because the target entity is not fetched, when I set the value only on the id.
Let's take an example: I have an Account table and DeviceConfig table. Inside the DeviceConfig's class definition, I add account in a many-to-one relation and accountId in relation with #MapsId.
Now when creating, I always set a value to accountId, but never the value is picked up, and the backend throws an SQL error, because the field cannot be null.
#Table(name = "djl_device_config")
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
public class DeviceConfig extends CoreEntity {
...
#JsonIgnore
#ManyToOne
#MapsId("accountId")
#JoinColumn(name = "account_id")
private Account account;
#Column(name = "account_id", insertable = false, updatable = true, nullable = true)
private UUID accountId;
}
So I suppose this is a config error on my side, but I've been reading the JPA for these three days and I still don't know what's wrong or what I should do to achieve the behaviour I expect.
That for any help you'll provide.
i'm using Spring Boot 2.4.2 and Data module for JPA implementation.
Now, i'm using an Oracle View, mapped by this JPA Entity:
#Entity
#Immutable
#Table(name = "ORDER_EXPORT_V")
#ToString
#Data
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class OrderExportView implements Serializable {
private static final long serialVersionUID = -4417678438840201704L;
#Id
#Column(name = "ID", nullable = false)
#EqualsAndHashCode.Include
private Long id;
....
The view uses an UNION which allows me to obtain two different attributes of the same parent entity, so for one same parent entity (A) with this UNION I get the attribute B in row 1 and attribute C in row 2: this means that the rows will be different from each other.
If I run the query with an Oracle client, I get the result set I expect: same parent entity with 2 different rows containing the different attributes.
Now the issue: when I run the query with Spring Data (JPA), I get the wrong result set: two lines but duplicate.
In debug, I check the query that perform Spring Data and it's correct; if I run the same query, the result set is correct, but from Java/Spring Data not. Why??
Thanks for your support!
I got it! I was wrong in the ID field.
The two rows have the same parent id, which is not good for JPA, which instead expects a unique value for each line.
So, now I introduced a UUID field into the view:
sys_guid() AS uuid
and in JPA Entity:
#Id
#Column(name = "UUID", nullable = false)
#EqualsAndHashCode.Include
private UUID uuid;
#Column(name = "ID")
private Long id;
and now everything works fine, as the new field has a unique value for each row.
I'm using SpringBoot 2.2.6 with JPA and I'm run into following problem:
#Transactional
public void batch() {
....
....
repository.save(data) // this is an update
....
....
repository.save(data) // this is a normal save
}
the Hibernate logging says to me that the save is executed before the update and this generate a constraint violation error on my db.
Do you have some idea why happend something like this?
Thanks
UPDATE
The Entity is something like this, clearly there are other Entity nested but the logic is similar
#Id
#Column(name="id")
#GeneratedValue(generator = "domande_dom_stati_domanda_id_seq", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "domande_dom_stati_domanda_id_seq", sequenceName = "domande_dom_stati_domanda_id_seq",allocationSize=1)
private Integer id;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne
#JoinColumn(name="id_dom_stato_domanda", nullable=false)
private DomStatoDomanda domandaStatoDomanda;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne
#JoinColumn(name="id_domanda", nullable=false)
private Domanda domande;
#Temporal(TemporalType.DATE)
#Column(name="data_validita")
private Date dataValidita;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="data_registrazione")
private Date dataRegistrazione;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="data_registrazione_fine")
private Date dataRegistrazioneFine;
#Column(length=50)
private String utente;
#Column(length=250)
private String note;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne
#JoinColumn(name="id_ruolo", nullable=false)
private Ruolo ruolo;
JPA/Hibernate queues the operations in its session whenever possible, does not call the database instantly and then just before the transaction is completing, order those operations based on type and execute them. This is called Transactional write-behind in hibernate. As you can see, even though you called the insert last, hibernate will order it as first if it was queued.
Inserts, in the order they were performed
Updates
Deletion of collection elements
Insertion of collection elements
Deletes, in the order they were performed
You can tell hibernate to flush it rather than queue it. So replace repository.save(data) with repository.saveAndFlush(data) so it executes in the order you wanted
Reference
Executions Order
I have created many entities in jpa.
When i checked in the database, i don't see any foreign key.
#Entity
public class Lodger implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long lodgerId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
private List<AccountOperation> accountOperationList;
...
}
#Entity
public class AccountOperation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long accountOperationId;
#ManyToOne
#JoinColumn(name = "lodger_id")
private Lodger lodger;
...
}
In this example i was thinking to get a foreign key in the account operation class.
table automaticaly created
http://www.wepaste.com/table_example/
Why?
Hibernate does not automatically generate foregein keys when generating dlls. I would recommend to turn of the generate-dll option as it may create inconsistent databases as the complexity increases. also check out either spring boot default database administration options:
Flyway
Liquibase
The main difference between the two relies in the fact that while both may be administrared with SQL, Liquibase offers a more database agnostic formats such as XML, and YML for the creation of your database
I faced the problem when I need to partially udate data in BD.
What I have:
I have three linked entities:
Profile --(1-m)--> Person --(1-1)--> Address
Where Person -> Address is lazy relationship. It was achieved via optional=false option (that allow hibernate to use proxy).
What the problem:
I need to update Profile in such way, that I needn't pull all Addresses that linked with this profile.
When I update Profile (don't work):
profile.setPersons(persons);
session.saveOrUpdate(profile);
throws: org.springframework.dao.DataIntegrityViolationException: not null property references a null or transient value
It happens because Person->Address relationship has optional=false option
I need to do:
//for each person
Address address = requestAddressFromDB();
person.setAddress(address);
persons.add(person)
//and only then
profile.setPersons(persons);
session.saveOrUpdate(profile);
profile.setPerson(person)
But I don't want to pull all address each time I update Profile name.
What is the question:
How can I avoid obligatory Person->(not null)Address constraint to save my profile without pulling all addresses?
ADDITION:
#Entity
public class Person{
#Id
#SequenceGenerator(name = "person_sequence", sequenceName = "sq_person")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_sequence")
#Column(name = "id")
private long personID;
#OneToOne(mappedBy="person", cascade=CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
private Address address;
//.. getters, setters
}
#Entity
public class Address {
#Id
#Column(name="id", unique=true, nullable=false)
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters=#Parameter(name="property", value="person"))
private long personID;
#PrimaryKeyJoinColumn
#OneToOne
private FileInfo person;
}
Modify the cascade element on the #OneToOne annotation so that the PERSIST operation is not cascaded. This may require you to manually persist updates to Address in certain areas of your code. If the cascade is not really used however no change is needed.
#OneToOne(mappedBy="person", cascade={CascadeType.MERGE, CascadeType.REMOVE, CascadeType.REFRESH}, optional = false, fetch = FetchType.LAZY)
private Adress address; //Do you know that Address is missing a 'd'?