Hi hibernate (envers) experts!
I currently have two/three entities
Automations which represent an automated process for one answer of a question (of a survey)
Answers answer of a user for a question (of a survey)
BaseEntity which is extended by Automations and Answers an has basic attributes like modify timestamps and so on.
The Automations table is audited by hibernate envers.
I want to fetch all deleted Automations for one specific Answer. The generated and executed query from hibernate does include my condition for the "deleted" revtype of envers but not my condition for the answer.
See the function listHistoryByAnswer
#MappedSuperclass
public class BaseEntity extends PanacheEntityBase {
#Id
#Column(name = "UUID", updatable = false, nullable = false, columnDefinition = "uuid")
private UUID _uuid;
#Column(name = "CREATED_AT", nullable = false, updatable = false, columnDefinition = "TIMESTAMP without time zone")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "CREATED_BY", nullable = false, updatable = false)
private String createdBy;
#Column(name = "UPDATED_AT", columnDefinition = "TIMESTAMP without time zone")
#UpdateTimestamp
private Timestamp updatedAt;
#Column(name = "UPDATED_BY")
private String updatedBy;
}
#Entity
#Table(name = "AUTOMATION", uniqueConstraints = #UniqueConstraint(columnNames = {"ANSWER_UUID"}))
#Audited
public class Automation extends BaseEntity {
#Basic
#Column(name = "DESCRIPTION")
private String description;
#JsonBackReference("automation-answer")
#ManyToOne
#JoinColumn(name = "ANSWER_UUID", nullable = false)
private Answer answer;
}
#Entity
#Table(name = "ANSWER")
#Audited(targetAuditMode = NOT_AUDITED)
public class Answer extends BaseEntity {
#JsonManagedReference("automation-answer")
#OneToMany(mappedBy = "answer", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Automation> automations = new ArrayList<>();
#Column(name = "DATA", columnDefinition = "jsonb")
private JsonNode data;
}
/**
* List old (deleted) questions based on the answer
* #param answerUuid unique id of the answer
* #return list with deleted questions
*/
public List<Automation> listHistoryByAnswer(final UUID answerUuid){
List resultList = AuditReaderFactory.get(getEntityManager())
.createQuery()
.forRevisionsOfEntity(Automation.class, true)
.add(AuditEntity.revisionType().eq(RevisionType.DEL))
.add(AuditEntity.property("answer_uuid").eq(answerUuid))
.getResultList();
return resultList;
}
Generated SQL
select automation0_.UUID as uuid1_1_0_,
automation0_.REV as rev2_1_0_,
defaultrev1_.REV as rev1_15_1_,
automation0_.REVTYPE as revtype3_1_0_,
automation0_.ANSWER_UUID as answer_u9_1_0_,
defaultrev1_.REVTSTMP as revtstmp2_15_1_
from A_AUTOMATION_HIS automation0_
cross join
REVINFO defaultrev1_
where automation0_.REVTYPE = 2 -- DELETED entities
and automation0_.REV = defaultrev1_.REV
order by automation0_.REV asc;
Related
I have two entities Space as parent and SpaceInactivityDate as child with relation #OneToMany and #ManyToMany and with space_id as a foreign key.
The insert operation works fine but when I tried to update, the Space table was updated but in the SpaceInactivityDate table instead of updating rows he added new lines. I have used the CascadeType=All and cascade = {CascadeType.PERSIST,CascadeType.MERGE} but usually I had the same result.
Space.java
#Entity
#Table(name = "space")
public class Space {
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, mappedBy = "space", fetch = FetchType.LAZY)
private Set<SpaceInactivityDate> spaceInactivityDates = new HashSet<SpaceInactivityDate>();
// getters+setters
}
SpaceInactivityDate .java
#Table(name="space_incativity_date")
#Entity
public class SpaceInactivityDate {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer id;
#Column(name = "inactivity_start_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityStartDate;
#Column(name = "inactivity_end_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityEndDate;
#JoinColumn(name="space_id",referencedColumnName="id")
#ManyToOne(optional=false,fetch=FetchType.LAZY)
private Space space;
}
SpaceServiceImpl.java
this.spaceDao.save(space);
I'm having an issue with JPA and Hibernate. I have 2 classes (actually more but those are the 2 that are giving me a headache).
They have a OneToMany/ManyToOne relationship, and I have specified on the OneToMany that I want it to cascade every possible change (CascadeType.ALL).
However, when I let HBM2DDL run in "update" mode, it is creating foreign key constraints that are not fitting what I want to achieve. It is creating a constraint in RESTRICT mode, which is absolutely not what I have specified. Perhaps the error is in my code ? I've joined it below.
#Entity
public class Department {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#OneToMany(mappedBy = "departmentByDepartment", cascade = CascadeType.ALL)
private Collection<Article> productsById;
#OneToMany(mappedBy = "departmentByDepartment", cascade = CascadeType.ALL)
private Collection<User> usersById;
And then :
#Entity
#Table(name = "product", schema = "qlog_project", catalog = "")
public class Article {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description", nullable = false)
private String description;
#Column(name = "price", nullable = false)
private double price;
#Column(name = "image", nullable = false)
private String image;
#ManyToOne
#JoinColumn(name = "department", referencedColumnName = "id", nullable = false)
private Department departmentByDepartment;
#OneToMany(mappedBy = "productByProductId", cascade = CascadeType.ALL)
private Collection<Stock> stocksById;
Also, I cannot seem to be able to delete an Article instance with EntityManager.remove(). It's not producing any query and not doing anything. Maybe it is linked ?
Thanks in advance,
Regards
In my project I have to connect to existing database and do logic which updates two tables.
My setup is following:
#Entity
#Table(name = "DOCUMENTCONTENT")
#Getter
public class DocumentContent {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#OneToOne
#JoinColumn(name = "DOCUMENT_ID", insertable = false, updatable = false)
private Document document;
#Lob
#Column(name = "CONTENT")
#Setter
private byte[] content;
}
#Entity
#Table(name = "DOCUMENT")
#Getter
public class Document {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#OneToOne(mappedBy = "document")
private DocumentContent documentContent;
#OneToMany(mappedBy = "document", fetch = EAGER)
private List<Attachment> attachments;
}
#Entity
#Table(name = "ATTACHMENT")
#Getter
public class Attachment {
#Id
#Column(name = "ID")
private Long id;
#ManyToOne
#JoinColumn(name = "DOCUMENT_ID", insertable = false, updatable = false)
private Document document;
#ManyToOne
#JoinColumn(name = "CONTRACT_ID",updatable = false, insertable = false)
private Contract contract;
}
#Entity
#Table(name = "CONTRACT")
#Getter
public class Contract {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#Column(name = "STATUS")
#Setter
private String status;
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customer customer;
#OneToMany(mappedBy = "contract", fetch = EAGER)
private List<Attachment> attachments;
}
#Service
public class MyServiceImpl implements MyService {
#Autowired
private DocumentContentRepository documentContentRepository; // spring data Crud Repository
#Override
#Transactional
public void updateDocumentContent(SomeDto someDto) {
DocumentContent documentContent = documentContentRepository.findByDocumentId(someDto.getDocumentId());
documentContent.setContent(someDto.getBytes());
List<Contract> contracts = documentContent.getDocument().getAttachments()
.stream().map(Attachment::getContract).collect(toList());
contracts.forEach(contract -> contract.setStatus("SIGNED"));
documentContentRepository.save(documentContent);
}
}
When I fire method from above service I can notice those SQL in console output:
Hibernate: update documentcontent set content=? where id=?
Hibernate: update contract set status=? where id=?
I understand why jpa performed first update in documentcontent table, but I don't know why it did update in contract table aswell. As you can see I didn't use CascadeType.MERGE in any entity.
Can you explain me why this second update has been performed without declaring cascade type?
I doubt it has anything to do with Cascade at all, but with transactional write behind mechanism (more info). I believe you could also get rid of the line
documentContentRepository.save(documentContent);
since you are modifying two managed entities. At the end of the transaction hibernate persists all entities marked as modified by the dirty checking mechanism (more info).
You are getting 2nd query for the reason, you are modifying Status property of Contract.
JPA detect this change and try to update entity.
This is default CaseCadeType behaviour of #OneToMany
For further reading follow this link.
[using JPA, MySQL, MVC, Servlets, JSP]
If I read some data from database LEFT JOIN-ing three tables (inside method of DAO object) how should I format that result, so i can set it as request attribute (in servlet) and forwards to JSP page?
Entities(tables in db):
Post:
#Entity
#Table(name = "post")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "post_id", unique = true, nullable = false)
private Integer id;
#Column(name = "post_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "post_date", unique = false, nullable = false)
private Date date;
#Column(name = "post_summary", length=1000, unique = false, nullable = true)
private String summary;
#Column(name = "post_content", length=50000, unique = false, nullable = false)
private String content;
#Column(name = "post_visitors", unique = false, nullable = false)
private Integer visitors;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "category_id", referencedColumnName="category_id", nullable = false)
private Category category;
#OneToMany(cascade = { ALL }, fetch = LAZY, mappedBy = "post")
private Set<Comment> comments = new HashSet<Comment>();
...
Entity Comment:
#Entity
#Table(name = "comment")
public class Comment implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "comment_id", unique = true, nullable = false)
private Integer id;
#Column(name = "comment_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "comment_date", unique = false, nullable = false)
private Date date;
#Column(name = "comment_content", length=600, unique = false, nullable = false)
private String content;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "post_id", referencedColumnName="post_id", nullable = false)
private Post post;
...
Entity User:
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer id;
#Column(name = "user_name", length=45, unique = false, nullable = false)
private String name; // "first_name" + ' ' + "last_name"
//URL address to user's image
#Column(name = "user_image", length=500, unique = false, nullable = true)
private String image;
#Column(name = "user_username", length=45, unique = false, nullable = false)
private String username;
#Column(name = "user_password", length=45, unique = false, nullable = false)
private String password;
...
So, I would like to make a method, probably inside PostDAO object that will look something like this:
public <SomeDataTypeFormat???> getPostsSummaries(){
Query q = em.createNativeQuery("SELECT
post_title,
post_summary,
post_date,
COUNT(comment_id) AS comment_cnt,
user.user_name
FROM
post
LEFT JOIN user USING(user_id)
LEFT JOIN comment USING(post_id)
GROUP BY
post_id
ORDER BY
comment_cnt DESC");
...
}
Method returns some fields from all three tables in database.
Do I need to make separate class and store those data in objects of that class? Or JSON (although I haven't worked with it yet)?
What is the practice? What is easiest data format to use and forward from servlet to JSP, for some fields gotten as a result of joining couple tables?
It depends on your objective; to get the data to the browser, JSON and AJAX isn't a bad choice. To get the data to the JSP (from the Servlet), you'll probably want a Data Transfer Object (or possibly an immutable Value Object).
I have a problem with hibernate query, that filters by columns from multiple entities inherited from common parent.
Here is example:
abstract parent:
#Entity
#Inheritance (strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AItem
{
/** The id. */
#Id
#Column (name = "pk_id")
private Long id;
/** The date of creational. */
#Column (name = "c_date_created", nullable = true, columnDefinition = "timestamp with time zone")
private Calendar dateCreated;
/** The date of change. */
#Column (name = "c_date_changed", nullable = true, columnDefinition = "timestamp with time zone")
private Calendar dateChanged;
/** Name of item. */
#Column (name = "c_name", nullable = false)
private String name;
/** type of item. */
#Column (name = "c_type", nullable = false)
#Enumerated (EnumType.STRING)
EItemType type;
}
children:
#Entity
#Table (name = "t_product", schema = "am")
public class CProduct extends AItem
{
#ManyToMany (fetch = FetchType.LAZY, mappedBy = "products")
private Set<CCampaign> campaigns;
...some more attributes...
}
#Entity
#Table (name = "t_service", schema = "am")
public class CService extends AItem
{
#ManyToMany (fetch = FetchType.LAZY, mappedBy = "services")
private Set<CCampaign> campaigns;
...some more attributes...
}
ccampaign:
#Entity
#Table (name = "t_campaign", schema = "am")
public class CCampaign
{
/** The id. */
#Id
#SequenceGenerator (name = "T_CAMPAIGN_PKID_GENERATOR", sequenceName = "AM.T_CAMPAIGN_PK_ID_SEQ")
#GeneratedValue (strategy = GenerationType.AUTO, generator = "T_CAMPAIGN_PKID_GENERATOR")
#Column (name = "pk_id")
private Long id;
/**
* Products available in the campaign.
*/
#ManyToMany (fetch = FetchType.EAGER, cascade = {CascadeType.REFRESH, CascadeType.MERGE})
#JoinTable (name = "t_x_campaign_product", schema = "am", joinColumns = {#JoinColumn (name = "pk_campaign", nullable = false, updatable = false)}, inverseJoinColumns = {#JoinColumn (name = "pk_item", nullable = false, updatable = false)})
private Set<CProduct> products;
/**
* services available in the campaign.
*/
#ManyToMany (fetch = FetchType.EAGER, cascade = {CascadeType.REFRESH, CascadeType.MERGE})
#JoinTable (name = "t_x_campaign_service", schema = "am", joinColumns = {#JoinColumn (name = "pk_campaign", nullable = false, updatable = false)}, inverseJoinColumns = {#JoinColumn (name = "pk_item", nullable = false, updatable = false)})
private Set<CService> services;
}
I want all items (services+products) returned for that campaign.
I have query
select distinct item from AItem item left join item.campaigns camp where camp.id = 5
now what hibernate does is it returns all related products,but not single service.
there are assigned services to campaign.
when i run query without specifiing camp.id then query will return services .
can somone help solve this?.
Using of Hibernate filter will solver your problem, see here See the questions answer of this
annotation to filter results of a #OneToMany association