I have an entity classes in JPA as below in which maId acts as a primary key and foreign key for many other tables.
#Table(name = "Table1")
public class Test implements Serializable {
#Id
#Column(name = "maId", updatable = false, nullable = false)
private String id;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="maId")
private MFieldData fieldData;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="maId")
private MPS mps;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="maId")
private MJob mJob;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="maId")
private List<MExtension> mlExtensions;
private Date createdDate;
private Date lastUpdatedDate;
}
Now,this is my another entity.
#Table(name = "table 2")
public class Test implements Serializable {
#Id
#Column(name = "maId", updatable = false, nullable = false)
private String maId;
private Integer cmd;
private String routeId;
}
By the time I receive a request this is API. I need to Insert the data across multiple tables.
How to implement a custom UUID (maId) generator and use it in #Id?
I need to use the same maId which is the unique id for this request across multiple entities while inserting into the DB. I am using a dirty way to do that. But is there any JPA way to solve this problem?
Any help would be highly appreciated!
The request has to pass the UUID (Called correlation-id) to track. https://docs.oracle.com/javase/7/docs/api/java/util/UUID.html can be used. If you generate in code it may go unknown to caller. The correlation-id is used to track the request data across systems it may travel due to event publish as well.
Related
I'm trying to use Hibernate to map the following relationship:
Each order contains 2 images. When I delete an order I want the images gone as well.
I have two entities, OrderItems and Image and they look like this
public class OrderItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Long id;
#Transient
private String language;
#OneToMany(fetch = FetchType.EAGER ,orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "order")
private List<Image> images ;
}
public class Image implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Long id;
#Column(name = "IMAGE_NAME")
private String name;
#Column(name = "IMAGE_BYTES", unique = false, nullable = true, length = 1000000)
private byte[] image;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id" , nullable = false)
private OrderItems order;
}
Inserting new orders will also insert the coresponding images but when I try to delete an order I get an foreign key constraint error from the tables Image
Am I missing something about Hibernate ? Shouldn't the attribute cascade = CascadeType.ALL do the trick ?
Thanks for taking the time to provide any feedback. Cheers
I already tried OneToMany and ManyToOne unidirectional and bidirectional but I get the same foreign key violation error or my images are not saved at all when I save a new order.
I solved the issue by using Spring to delete an order and automagically it also deleted the images corresponding to that order.
So my first approach of deleting orders by executing sql queries directly on the DB was the issue.
Try like this
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "order_id", nullable = false)
I have the following Hibernate entity:
#Entity
#Table(name = "jobs")
public class Job {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jobs_j_id_seq")
#SequenceGenerator(sequenceName = "jobs_j_id_seq", name = "jobs_j_id_seq", allocationSize = 1)
#Column(name = "j_id")
private Long id;
#Column(name = "j_description", length = 300, nullable = false)
private String description;
#Column(name = "j_category", length = 50, nullable = false)
#Enumerated(EnumType.STRING)
private JobCategory category;
#Column(name = "j_job_provided", length = 50, nullable = false)
private String jobProvided;
#ManyToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "j_provider_id")
private User provider;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "job")
private Set<Review> reviews;
#Transient
private Long averageRating;
.
.
.
}
What is the correct way of computing the value of the averageRating variable? I've read about #Formula, but I understand this only works the first time the entity is fetched. Meaning that if a new review is added to the Job instance, JPA will update the job but the #Formula will not run again, leading to my value not being recomputed.
Is there a way of having a dynamic #Formula, that will react to changes?
I can always iterate through the reviews and calculate the averageRating, but as we all know this is not the way to go.
If you want to denormalize the schema, you can add an actual column on the table and handle the update with SQL triggers. Then you just annotate the property with #Generated(GenerationTime.ALWAYS) and Hibernate will after every update refresh that property. This might work with #Formula as well, but I would advise against this. What is the point of having this average? IMO you should just always compute it on demand and think about storing it only if that becomes a performance issue.
I am facing an issue with Spring Data repository + Hibernate (in spring 2.1.4)
Note: Everything was working fine in spring 1.5.x
The problem is with #ManyToOne(fetch = FetchType.Lazy) I get correct records in 1.5 but error in spring 2.0 but with #ManyToOne(fetch = FetchType.Eager) I get duplicate records in List<Stories>.
I am using Spring data repositories epicRepository.findById(Long id) (previously Spring had epicRepository.find() but they removed it in spring 2.x)
I don't want to use #Fetch (FetchMode.SELECT) solution as it has to execute multiple select queries and will become very non-performant.
Problem:
if I use fetch = FetchType.Lazy i am getting an error could not
initialize proxy - no session (only started after upgrading to
spring 2.x and returned correct number of rows) [This error seems to be ok as I am trying to fetch the list.count later in the code, and there are no duplicates when I checked through debugger before the erroring line]
so based on some solutions here in SO I used FetchType.Eager
(I understand the performance implications of this, but anyway I
needed to do this for another work so I did this because this list is smaller and need to preserve some business logic during updates). But Now I am getting duplicate records.
Even If what I do is wrong, the count in list (mean the duplicates) should not be wrong ?
I have the following JPA entities / tables
Epic -> [id (pk), name, status_id (fk))
Story->[id (pk), name, status_id (fk), epic_id (fk))
Task -> [id (pk), name, resolution_type_id (fk), story_id (fk))
forgive me if there any typos (as i recreated code manually using different use case)
Epic.java
#Data
public class Epic {
#Id
private Long id;
private String name;
#OneToOne(fetch = FetchType.Eager, optional = false)
#JoinColumn(name = id, referenceColumnName = 'id', nullable = false, insertable = true, updatable = true)
private Status status;
#OneToMany(fetch = FetchType.Eager, cascade = ALL, mappedBy = epic)
private List<Story> stories;
}
Story.java
#Data
public class Story {
#Id
private Long id;
private String name;
#OneToOne(fetch = FetchType.Eager, optional = false)
#JoinColumn(name = id, referenceColumnName = 'id', nullable = false, insertable = true, updatable = true)
private Status status;
#OneToMany(fetch = FetchType.Eager, cascade = ALL, mappedBy = epic)
private List<Task> tasks;
#ManyToOne(fetch = FetchType.Lazy)
// This is the problem area
// Error if FetchType.Eager
// But duplicates in FetchType.Lazy
#JoinColumn(name = "id", nullable = false)
private Epic epic;
}
Task.java
#Data
public class Task {
#Id
private Long id;
private String name;
#ManyToOne(fetch = FetchType.Lazy)
#JoinColumn(name = "id")
private Story story;
#OneToOne (fetch = FetchType.Eager, optional = true)
#JoinColumn (name = "id", )
private Resolution resolution;
}
This question has been replied to many times before. Your options could be:
all EAGER collections you should migrate to Sets
manually do duplicate filtering using stream.distinct.collect on getters of collections.
Scenario:
I have a products table with these fields: id, code, description, photo.
I have a product_stock view from other schema with fields: prod_code, stok_tot
Misson: What I need to do in Spring Boot is to join the products with their total stock.
Rules:
I can't use #Query on this task, I need to join them at the Product Entity Class, so that stock became some kind of Transient field of product.
I can't change the fact that product's ID is a Long and ProductStock's ID is a String, but I could use product's code field instead right? (how?)
So far... I tryed to use #OneToOne and #JoinColumn to do the job, but my REST gives me the stock field as NULL.
"Estoque.java"
#Entity
#Table(name = "VW_ESTOQUE", schema = "ASICAT")
public class Estoque {
#Id
#Column(name = "CD_BEM_SERVICO", unique = true, nullable = false)
private String codigo;
#Column(name = "ESTOQUE")
private Long estoque;
// getters and setters hidden by me
}
"Produto.java"
#Entity
#NamedEntityGraph(name = "Produto.detail", attributeNodes = #NamedAttributeNode("categorias"))
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// here is where I get the null values
#Transient
#OneToOne(fetch = FetchType.LAZY)
#JoinTable(name = "VW_ESTOQUE", joinColumns = #JoinColumn(name = "CODIGO", referencedColumnName = "CODIGO"), inverseJoinColumns = #JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO"))
private Estoque estoque;
private String hash;
#ManyToMany(mappedBy = "produtos", fetch = FetchType.EAGER)
#BatchSize(size = 10)
private List<Categoria> categorias = new ArrayList<>();
// getters and setters hidden by me
}
In my product repository I call FindAll()
You have annotated Produto.estoque as #Transient, which means that it is not part of the persistent state of the entity. Such a field will be neither written nor read when instances of that entity are managed. That's not going to serve your purpose.
There are two things I can imagine you might have been trying to achieve:
That every time an Estoque is accessed via a Produto, it should be loaded from the DB to ensure its freshness. JPA does not provide for that, though you might want to annotate Estoque with #Cacheable(value = false), and specify the lazy fetch strategy on the Produto side of the relationship.
You want to avoid the persistence provider attempting to persist any changes to an Estoque, since it is backed by a view, not an updatable table. This we can address.
My first suggestion would be to map ASICAT.VW_ESTOQUE as a secondary table instead of an entirely separate entity. That might look something like this:
#Entity
#SecondaryTable(name = "VW_ESTOQUE", schema = "ASICAT"
pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "CD_BEM_SERVICO",
referencedColumnName = "CODIGO") })
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
#Column(name = "ESTOQUE", table = "VW_ESTOQUE", nullable = true,
insertable = false, updatable = false)
private Long estoque;
// ...
}
You might furthermore avoid providing a setter for the estoque property.
But the SecondaryTable approach might not work well if you cannot rely on the ESTOQUE view always to provide a row for every row of PRODUTO, as there will very likely be an inner join involved in retrievals. Moreover, you don't get lazy fetches this way. The main alternative is more or less what you present in your question: to set up a separate Estoque entity.
If you do set up a separate Estoque, however, then I would approach it a bit differently. Specifically,
I would make the relationship bidirectional, so that I could
make the Estoque entity the relationship owner.
Something like this, then:
#Entity
public class Produto implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// must not be #Transient:
#OneToOne(fetch = FetchType.LAZY, mappedBy = "produto", cascade = {
CascadeType.REFRESH
})
private Estoque estoque;
// ...
}
#Entity
#Table(name = "VW_ESTOQUE", schema = "ASICAT")
#Cacheable(value = false)
public class Estoque {
#Id
#Column(name = "CD_BEM_SERVICO", nullable = false,
insertable = false, updatable = false)
private String codigo;
#Column(name = "ESTOQUE", insertable = false, updatable = false)
private Long estoque;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO",
nullable = false, insertable = false, updatable = false, unique = true)
Produto produto;
// getters and setters hidden by me
}
In this case, I would avoid providing setter methods for any of the properties of Estoque, and avoid providing any constructor that allows initial property values to be set. Thus, to a first approximation, the only
way an instance's properties will take non-null values is if they are set by the persistence provider.
Additionally, since you mention Oracle, if you are using TopLink as your persistence provider then you might consider applying its #ReadOnly extension attribute to the Estoque entity, in place of or even in addition to some of these protections against trying to insert into or update the view.
I am trying to build a bidirectional one to many relationship with the spring data jpa but the list annotated with #onetomany always return one element.
Here is the code for my entities(setters and getters omitted):
#Entity
#Table(name = "sdk_sdk")
public class SDKEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String version;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "sdk")
#OrderBy("order ASC")
private List<SDKFileEntity> fileEntities;
}
And the second entity:
#Entity
#Table(name = "sdk_file")
public class SDKFileEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String fileType;
private Integer sdkId;
public SDKFileEntity() {
}
#ManyToOne
#JoinColumn(name = "id", insertable = false, updatable = false)
private SDKEntity sdk;
I am trying to have a manytoone mapping where the sdkId corresponds to the id from the SDKEntity class.
Whenever I try to get the sdkfiles from the sdkEntity using spring's repository, the size of the list is always 1.
So for example:
SDKEntity entity=repository.findOne(foo);
List<SDKFileEntity> files=entity.getFileEntities();
here the size of files is 1, I have to delete the first element from the database to obtain the second element.
For me the reason here was that a parent entity implemented equals and hashcode
and unfortunately in a way that all existing entities were equal.
And non of the child entities implemented it herself.
So then the #OneToMany relation returned only the first element.
Took me quite some time.
This part of Code looks suspicious
#ManyToOne
#JoinColumn(name = "id", insertable = false, updatable = false)
private SDKEntity sdk;
name = "id" it should be actual column name as written in database column name like this
#JoinColumn(name = "VISIT_ID", referencedColumnName = "ID")
#ManyToOne
private Visit visitId;