I have an entity FamilyEntity which contains a transient field HouseEntity and a non transient field houseId.
#Column
#NotNull
#Type(type = "uuid-char")
private UUID houseId;
#Transient
private HouseEntity house;
How can I load the HouseEntity (and avoid LazyInitalizationExcepetion) without removing the transient annotation from HouseEntity? (It's older code where it would be rough to remove transient)
Related
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
In other words, the common Jackson markup is not enough for serializing the same entity for using as the REST request response to the Angular frontend and to pass the object to Elasticsearch via the Jest client. Say, I have an image in the Entity as a byte array, and I'd like it to be stored to DB and be passed to the frontend, but don't like it being indexed by Elasticsearch to reduce the costs or quotas.
Now I have to use Jackson's JsonView to markup the fields to use for the Spring Data Elasticsearch's ObjectMapper:
#Entity
#Table(name = "good")
#org.springframework.data.elasticsearch.annotations.Document(indexName = "good", shards = 1, replicas = 0, refreshInterval = "-1")
public class Good implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#org.springframework.data.elasticsearch.annotations.Field(type = FieldType.Keyword)
#JsonView(Views.Elasticsearch.class)
private Long id;
#NotNull
#Column(name = "short_name", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Text)
private String shortName;
#NotNull
#Column(name = "description", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Text)
private String description;
#Lob
#Column(name = "image", nullable = false)
#JsonView(Views.Rest.class)
private byte[] image;
#Column(name = "image_content_type", nullable = false)
#JsonView(Views.Rest.class)
private String imageContentType;
#NotNull
#Column(name = "price", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Integer)
private Integer price;
...
I have a clss for Views:
public class Views {
public static class Rest{}
public static class Elasticsearch{}
}
And the ObjectMapper set up in the corresponding Bean:
#Override
public String mapToString(Object object) throws IOException {
log.trace("Object to convert to JSON : {}",object);
log.trace("Converting to json for elasticsearch >>> {}",objectMapper.writer().withView(Views.Elasticsearch.class).writeValueAsString(object));
//log.trace("Converting to json for elasticsearch >>> {}",objectMapper.writeValueAsString(object));
return objectMapper.writerWithView(Views.Elasticsearch.class).writeValueAsString(object);
//return objectMapper.writeValueAsString(object);
}
So, I have to markup all the fields except the ignored to Elasticsearch with #JsonView(Views.Elasticsearch.class) and this is the error prone. Also, this field still requires #Field usage if I like to pass some parameters there like store or value type. When I have #JsonView(Views.Elasticsearch.class), but don't have #Field on some, the fields are created in the index on a fly, that allows them to search, but not in desired way.
The latest is the reason why if I just leave #Field there and don't place it over fields I don't want to index into Elasticsearch, the initial index indeed ignores them, but later requests pass the undesired field when the entity is serialized exactly the same way as it is done for the REST. And the index property is created on a fly, making resources being spent for the large binary object indexing. So it looks like #Field is used for the initial index creation on the startup, but are not configured to be used with ObjectMapper of the Spring Data Elasticsearch.
So, I'd like to make this ObjectMapper take only fields with #Field above them into account, i.e serialize the fields marked with #Field only and use no #JsonView staff. How can I configure it?
These are known problems when using the Jackson Object Mapper in Spring Data Elasticsearch (which als is the default) and this is one of the reasons, why in Spring Data Elasticsearch as of version 3.2 (which currently is in RC2 and will be available as 3.2.0.GA in mid-september), there is a different mapper available, the ElasticsearchEntityMapper.
This mapper still has to be setup explicitly, the reference documentation of 3.2.0.RC2 shows how to do this. Using this mapper the Jackson annotations do not influence the data stored in and read from Elasticsearch. And you can use the org.springframework.data.annotation.Transient annotation on a field to not have it stored in Elasticsearch.
The #Field annotation is used to setup the initial Elasticsearch mapping, properties not having this annotation are automatically mapped by Elasticsearch when they are inserted.
I'm using #version annotation in spring data so I have a parent entity, and it has list of child entity. when I delete an element from child list the parent version doesn't increase. can anyone clarify for me this #version alternative,
why the versing in this case doesn't increase, is it a good way to manage versioning or should I use trasaction "lock".
in the documentation i read that the version update only on updating a row
in the databse but in my case i put version on parent entity and i want
note: i searched a lot in the internet but i didnt find a clear solution, can any one help me.
I assume you are using Hibernate. Lets say that the "UnderlyingPerTradingAccount" table has a column called "trading_account_id", which is a foreign key to the TradingAccount table. In order to achieve the behavior you described, you need to change the mapping. Can you try this:
public class TradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="trading_account_id", referencedColumnName = "trading_account_id", insertable = false, updatable = false)
private List<UnderlyingPerTradingAccount> underlyingPerTradingAccounts;
#Version
private Long version;
}
and
public class UnderlyingPerTradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="trading_account_id", nullable = false)
private TradingAccount tradingAccount;
private Boolean enableBuy;
private Boolean enableSell;
}
This should mark the parent entity as "dirty" when the child entity is updated and trigger the version increment.
However, I would think of some other method to track "version" changes of the parent entity as this would just cause an additional overhead and update statements to the parent.
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.
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'?