I am using Spring and Hibernate for my application.
I am only allowing logical delete in my application where I need to set the field isActive=false. Instead of repeating the same field in all the entities, I created a Base Class with the property and getter-setter for 'isActive'.
So, during delete, I invoke the update() method and set the isActive to false.
I am not able to get this working. If any one has any idea, please let me know.
Base Entity
public abstract class BaseEntity<TId extends Serializable> implements IEntity<TId> {
#Basic
#Column(name = "IsActive")
protected boolean isActive;
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
isActive= isActive;
}
}
Child Entity
#Entity(name="Role")
#Table(schema = "dbo")
public class MyEntity extends BaseEntity {
//remaining entities
}
Hibernate Util Class
public void remove(TEntity entity) {
//Note: Enterprise data should be never removed.
entity.setIsActive(false);
sessionFactory.getCurrentSession().update(entity);
}
Try to replace the code in setIsActive method with:
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
in your code the use of variable name without this could be ambiguos...
I think you should also add #MappedSuperclass annotation to your abstract class to achieve field inheritance.
The issue with the proposed solution (which you allude to in your comment to that answer) is that does not handle cascading delete.
An alternative (Hibernate specific, non-JPA) solution might be to use Hibernate's #SQLDelete annotation:
http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querysql.html#querysql-cud
I seem to recall however that this Annotation cannot be defined on the Superclass and must be defined on each Entity class.
The problem with Logical delete in general however is that you then have to remember to filter every single query and every single collection mapping to exclude these records.
In my opinion an even better solution is to forget about logical delete altogether. Use Hibernate Envers as an auditing mechanism. You can then recover any deleted records as required.
http://envers.jboss.org/
You can use the SQLDelete annotation...
#org.hibernate.annotations.SQLDelete;
//Package name...
//Imports...
#Entity
#Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
...
private char deleted;
Source: http://featurenotbug.com/2009/07/soft-deletes-using-hibernate-annotations/
Related
I'm having a problem with Specification as stated in the title.
Here is my FishingLocation class:
#Entity
#Table(name = "tbl_fishing_location")
public class FishingLocation {
...
#JsonIgnore
#OneToMany(mappedBy = "id", fetch = FetchType.EAGER)
private List<Lake> lakeList;
...
}
And here is my Lake class:
#Entity
#Table(name = "tbl_lake")
public class Lake {
...
#JsonIgnore
#ManyToOne
#JoinColumn
private FishingLocation fishingLocation;
...
}
They both have a StaticMetamodel as follow:
#StaticMetamodel(FishingLocation.class)
public class FishingLocation_ {
public static volatile ListAttribute<FishingLocation, Lake> lakeList;
public static volatile SingularAttribute<FishingLocation, Long> id;
}
#StaticMetamodel(Lake.class)
public class Lake_ {
public static volatile SingularAttribute<Lake, Long> id;
public static volatile SingularAttribute<Lake, FishingLocation> fishingLocation;
}
I have created a Specification to filter FishingLocation as follow (the fishing method is not relevant):
public static Specification<FishingLocation> fishingMethodIdIn(Set<Long> fishingMethodIds) {
if (fishingMethodIds == null || fishingMethodIds.isEmpty()) {
return null;
}
return root.join(FishingLocation_.lakeList)
.join(Lake_.fishingMethodList)
.get(FishingMethod_.id).in(fishingMethodIds);
};
}
The problem is that when I run the program and send a request to filter, Hibernate showed me this SQL query:
select
fishingloc0_.id as id1_7_,
fishingloc0_.active as active2_7_,
...
from
tbl_fishing_location fishingloc0_
.... (some inner joins)
inner join
tbl_lake lakelist4_
on fishingloc0_.id=lakelist4_.id
....
It is supposed to be on fishingloc0_.id=lakelist4_.fishing_location_id. So where is the problem in my code? Huge thanks to anyone helping me with this problem.
Edit: This is just a small fraction of my code. My filter has multiple criteria and join many tables so I cannot just use premade function in FishingLocationRepository. I want to create dynamic query which should be build depend on what user chooses to filter by.
you don't need to call this all using custom queries the Hibernate will take care of everything with OneTO Many and ManyTOOne mappings.
You can get the required data by simply calling the getter methods of its class.
for example, in the case of Fishing Location, you can get all the lakes related to that location by calling new FishingLocation().getLakeList().
same for the other case. https://www.javatpoint.com/hibernate-many-to-one-example-using-annotation
friends. I have a some problem. I hope you can help me. I have merchant table, model, repository and service.
Merchant.java
#Data
#Entity
#Table(name = "Merchant")
public class Merchant {
#Id
private String term;
private String tag;
#Column(name = "tag_full")
private String tagFull;
}
MerchantRepository.java
public interface MerchantRepository extends JpaRepository<Merchant, String> {
}
MerchantService.java
#Service
#RequiredArgsConstructor
public class MerchantService {
private final MerchantRepository merchantRepository;
public Merchant getOne(String term) {
return merchantRepository.getOne(term);
}
}
The getOne function works correctly if I send a value that is in the table. But if I send a value that is not in the table, I get an error. How can i resolve this problem?
You Should Use findeOne() Instead getOne().
We use getOne() when The Entity is available.
findOne() return Optional,Should Check If It Was Present , use get() to return Entity.
Please Use findeOne() Instead getOne(). I think it will work for you.
invoking getOne() may return a lazily fetched entity.
IntelliJ Data view will call entity's toString() method and proxy entity will ask database to get real entity.
Disable toString object view in IntelliJ preference
I am trying to use the #SQLDelete annotation of Hibernate to make soft deletion. It works well when the DB schema is static, i.e: passing it in the SQL.
Unfortunately, it seems the SQL is passed as is to EntityPersisters (cf EntityClass's method CustomSQL createCustomSQL(AnnotationInstance customSqlAnnotation) so I can't find a way to pass the schema name dynamically like in Native SQL queries using {h-schema}
Did anyone find a good workaround for this issue (I am using Hibernate 4.3.5)?
Edit: Unless there is a real solution, I ended up modifying the code source of org.hibernate.persister.entity.AbstractEntityPersister by replacing the schema placeholder when setting the custom SQL queries in method doLateInit.
Edit2: I have created an issue for this behaviour in Hibernate JIRA. I will create a pull request later today and I wish the Hibernate Team will accept it
Soft deletes using Hibernate annotations.
As linked author stated below:
I am currently working on a Seam application that has a need for soft deletes in the database. To the right you can see a snippet of my database diagram which contains a CUSTOMER and APP_USER table. This is just a straight forward one to many relationship but the important thing to note though is the “DELETED” field in each table. This is the field that will be used to track the soft delete. If the field contains a ‘1’ the record has been deleted and if it contains a ‘0’ the record hasn’t been deleted.
Before ORMs like Hibernate I would have had to track and set this flag myself using SQL. It wouldn’t be super hard to do but who wants to write a bunch of boilerplate code just to keep track of whether or not a record has been deleted. This is where Hibernate and annotations comes to the rescue.
Below are the 2 Entity classes that were generated by Hibernate using seamgen. I have omitted parts of the code for clarity.
Customer.java
//Package name...
//Imports...
#Entity
#Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
private Billing billing;
private String name;
private String address;
private String zipCode;
private String city;
private String state;
private String notes;
private char enabled;
private char deleted;
private Set appUsers = new HashSet(0);
// Constructors...
// Getters and Setters...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "customer")
// Filter added to retrieve only records that have not been soft deleted.
#Where(clause = "deleted <> '1'")
public Set getAppUsers() {
return this.appUsers;
}
public void setAppUsers(Set appUsers) {
this.appUsers = appUsers;
}
}
AppUser.java
//Package name...
//Imports...
#Entity
#Table(name = "APP_USER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE app_user SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class AppUser implements java.io.Serializable {
private long id;
private Customer customer;
private AppRole appRole;
private char enabled;
private String username;
private String appPassword;
private Date expirationDate;
private String firstName;
private String lastName;
private String email;
private String phone;
private String fax;
private char deleted;
private Set persons = new HashSet(0);
// Constructors...
// Getters and Setters...
}
The following 2 steps is all that I had to do to implement the soft delete.
Added the #SQLDelete annotation which overrides the default
Hibernate delete for that entity.
Added the #Where annotation to filter the queries and only return
records that haven’t been soft deleted. Notice also that in the
CUSTOMER class I added an #Where to the appUsers collection. This is
needed to fetch only the appUsers for that Customer that have not
been soft deleted.
Viola! Now anytime you delete those entities it will set the “DELETED” field to ‘1’ and when you query those entities it will only return records that contain a ‘0’ in the “DELETED” field.
Hard to believe but that is all there is to implementing soft deletes using Hibernate annotations.
Note:
also note that instead of using the #Where(clause="deleted ‘1’") statements you can use hibernate filter (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-filters) to globally filter-out all ‘deleted’ entities. I found that defining 2 entity managers (‘normal’ one that filter deleted items, and one that doesn’t, for the rare cases…) is usually quite convenient.
Using EntityPersister
You can create a DeleteEventListener such as:
public class SoftDeleteEventListener extends DefaultDeleteEventListener {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void onDelete(DeleteEvent event, Set arg1) throws HibernateException {
Object o = event.getObject();
if (o instanceof SoftDeletable) {
((SoftDeletable)o).setStatusId(1);
EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), o);
EntityEntry entityEntry = event.getSession().getPersistenceContext().getEntry(o);
cascadeBeforeDelete(event.getSession(), persister, o, entityEntry, arg1);
cascadeAfterDelete(event.getSession(), persister, o, arg1);
} else {
super.onDelete(event, arg1);
}
}
}
hook it into your persistence.xml like this
<property name = "hibernate.ejb.event.delete" value = "org.something.SoftDeleteEventListener"/>
Also, don't forget to update your cascades in your annotations.
Resource Link:
Hibernate: Overwrite sql-delete with inheritace
Custom SQL for CRUD operations
Custom SQL for create, update and delete
Use like this
#SQLDelete(sql = "UPDATE {h-schema}LEAVE SET STATUS = 'DELETED' WHERE id = ?", check = ResultCheckStyle.COUNT)
I think there are 2 way
First is to add:
app.datasource.schema=<schema_name>
to your application.properties.
The second is to use the schema in annotation to your table model
I am working on a huge application with a complex database schema. I am using Spring and Hibernate for the development.
I wanted to know how to soft-delete an entity(where the active field is there in a superclass rather than having in all the entities). I implemented the suggestion provided here.
Below is the structure of my entities and hibernate util classes
Base Entity
#MappedSuperclass
public abstract class BaseEntity<TId extends Serializable> implements IEntity<TId> {
#Basic
#Column(name = "IsActive")
protected boolean isActive;
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
isActive= isActive;
}
}
Child Entity :
#Entity(name="Role")
#Table(schema = "dbo")
public class Role extends BaseEntity {
//remaining fields
//1. foreign key reference to another entity
//2. List<Child> entities
//3. Self reference fields
}
Hibernate Util Class:
public void remove(TEntity entity) {
//Note: Enterprise data should be never removed.
entity.setIsActive(false);
sessionFactory.getCurrentSession().update(entity);
}
Now I have a few requirements with this which I am not able to solve now.
When I delete 'Role' entity, all the children entities should also get deleted (soft delete only for all) :-> Do I need to fetch the parent entity, iterate through the children and delete one by one ?
Role has a foreign-key reference with another entity 'Department'. If a department is deleted, the roles associated should get deleted conditionally(ie, only if the caller decides: in some cases, we dont want to delete the referred entities).
There are some self-referencing column like 'ParentRoleId'. If a Role is deleted, all its referenced roles also should be deleted. -> Do I need to fetch the ID and then delete all the self-referenced children entities and then delete each?
eg: Department can have a parent department(which is by using the field : parentdeptid). If I delete a parent department, all the sub-departments should get deleted
If anyone has any suggestions on how to do this, please let me know.
I try to log any changes of my JPA entities. For this reason each entity inherits from an abstract entity class which has a list of LogEntry objects.
AbstractEntity class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#EntityListeners(ChangeListener.class)
public abstract class AbstractEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Temporal(TemporalType.DATE)
private Date validFrom;
#Temporal(TemporalType.DATE)
private Date validTo;
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "abstractEntity")
private List<LogEntry> logEntry = new ArrayList<LogEntry>();
//getter and setter
}
LogEntry class:
#Entity
public class LogEntry extends AbstractEntity {
#ManyToOne
#JoinColumn
protected AbstractEntity abstractEntity;
#ManyToOne
#JoinColumn
protected Person person; // creator or updater
#Column(updatable=false, insertable=false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
protected Date changeDate;
protected String comment;
//getter and setter
}
My approach for this was to create a new LogEntry object and add it to the entity's LogEntry list before an entity got updated or persisted.
I tried the following solutions:
Using callback annotations (#PreUpdate, #PrePersist etc.) directly in the entity class or separated in an entity listener connected with AbstractEntity
Using EclipsLink's DescriptorEvent and the corresponding callback methods in an entity listener. This was the most promising trial. In preUpdate I could add a new LogEntry to the affected object. The added LogEntry was even correctly persisted, but preUpdate will be invoked by any database operation (select also leads to invocation of preUpdate), so I can't differ between a changed object and an object with no changes. The provided changesets from the descriptor event, the related query or the unitOfWork are in each case null. A comparison of the current object and the old object (provided by the descriptor event) is IMHO too complex, isn't it?
On the other hand, in preUpdateWithChanges I could easily detect a changed entity, but at this point it is apparently too late for adding a logentry. The logentry won't be persisted.
Nearly each of these trials enables me to change attributes (like name, or validTo) of the affected entity. But no solution provides the opportunity to create a new LogEntry instance, or rather to persist this LogEntry instance. I also tried to get an instance of a session bean via jndi lookup to persists the LogEntry manually. The jndi lookup works, but calling create or update methods of the session bean has no effect.
My current entity listener looks like this:
public class ChangeListener extends DescriptorEventAdapter {
#Override
public void preUpdate(DescriptorEvent event) {
AbstractEntity entity = (AbstractEntity) event.getObject();
if (!(entity instanceof LogEntry)) {
LogEntry logEntry = new LogEntry();
logEntry.setPerson(getSessionController().getCurrentUser());
logEntry.setAbstractEntity(entity);
entity.getLogEntries().add(logEntry);
}
}
}
Hibernate Envers is for various reasons no option.
EclipseLink Version is 2.3.2.
Persisting new object during events from the commit process can be difficult.
You could obtain the changes before the commit and persist you logs (get change set from the UnitOfWork).
See,
http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_access_what_changed_in_an_object_or_transaction.3F
Otherwise, it is possible to directly insert and object from inside an event.
i.e.
event.getSession().insertObject(logEntry);
Temporarily I solved this issue by comparing the current entity and the old entity in preUpdate. The comparison is done with EqualsBuilder from Apache Commons Lang.
public class ChangeAbstractEntityListener extends DescriptorEventAdapter {
#Override
public void preUpdate(DescriptorEvent event) {
AbstractEntity originalEntity = (AbstractEntity) event.getOriginalObject();
AbstractEntity entity = (AbstractEntity) event.getObject();
if (!EqualsBuilder.reflectionEquals(originalEntity, entity, true)) {
if (!(entity instanceof LogEntry)) {
LogEntry logEntry = new LogEntry();
logEntry.setPerson(getSessionController().getCurrentUser());
logEntry.setAbstractEntity(entity);
entity.getLogEntries().add(logEntry);
}
}
}
//jndi lookup to get the user object
}
You can try Tracking Changes Using History Policy
EclipseLink provides extended support for tracking all changes made to the database. The EclipseLink HistoryPolicy can be configured on a ClassDescriptor to store a mirror table of the original that will store the state of the object at any point in time. This can be used for auditing purposes, or to allow querying as of past points in time, or to allow restoring old data.
If you want simple entity auditing, then look no further than Hibernate's Envers module.
It works with JPA (and Spring-Data), and can store each changed version along with who changed it if you run say Spring Security.
Envers is part of Hibernate since 3.6