I am using Java, Hibernate, Spring Data and fairly new to this technology. I need to figure out how to Skip rows that are marked as 'archived.' We have a strict guidance from our DB architect that no rows shall be deleted from the database.
#MappedSuperclass
public class AbstractEntity implements Identifiable<String> {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy="uuid")
private String id;
private boolean archived; //<----?
}
#Entity
public class Employee extends AbstractEntity {
private String title;
private String fullName;
#ManyToOne
private Department dept;
}
#Entity
public class Department extends AbstractEntity {
private String name;
}
In the above example, any class extending AbstractEntity should never return rows that have archived == true. All my domain classes will be extending AbstractEntity so I'd like a solution that's either implemented in AbstractEntity.java or at some global configuration so that all generated SQL calls are 'where [table].archived <> true'
Take a look at Hibernate Filters.
#FilterDef(name="activeOnly")
#Filter(name="activeOnly", condition= "archived <> 1")
// assumes that all tables have a numeric column "archived"
// as you can notice, this is a filter at the SQL Level
// (not at the Entity level)
#MappedSuperclass
public class AbstractEntity // ....
I've never used Spring Data, but the Adding custom behavior to all repositories section of the official documentation lead me to belieave that it is quite easy to obtain an injected EntityManager and customize its behaviour. Just unwrap it and enable the filter.
Session session = entityManager.unwrap(Session.class);
session.enableFilter("activeOnly");
If you want the filter to be applied for all subclasses of the #MappedSuperclass use a recent version of Hibernate. Only version 3.5 and greater (see HHH-4332) supports this behaviour.
Also, there is one gotcha, you may need to repeat the filter on associations (See Hibernate Filters on related table with MappedSuperClass).
If you want to customize the delete operations as well, use #SQLDelete to mark archived = 1 (see Soft deletes using Hibernate annotations). But to the best of my knowledge this only works on mapped entities (nothing can be done at the #MappedSuperclass level)
Related
I have a class Health Check - as part of the class I record how many parasites are seen (enum NONE, SOME, MANY) and also the location of the parasites (enum HEAD, FEET, BODY).
Two ways this could be done:
METHOD 1
Health Check
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
private Parasite parasite;
Parasite
public class Parasite {
private BodyLocation bodyLocation;
private Coverage coverage;
}
Or I could have:
METHOD 2
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
private ParasiteNumber parasiteNumber;
private ParasiteLocation parasiteLocation;
Would method 1 require #Entity on parasite class and a entry in the table for each Health Check and a #OneToOne annotation?
Note Parasite class is not used anywhere else. Its only a part of a Health Check.
Which way is correct?
Generally speaking, yes. In ORM (aka JPA or Hibernate), you are building a graph of objects that represent things in your database. Anything that one #Entity touches is also an #Entity because it's a graph.
Whether it's a #OneToOne or a #ManyToOne, etc, depends on the nature of your data model. But, keep in mind, those connections can also be #Lazy, so they are not loaded until they are needed.
Because of #Lazy, method 2 might be preferred, idk. I assume ParasiteLocation and ParasiteNumber is some sort of join-table. If that's the case, you could load a HealthCheck with its PL and PN, but those objects could be Lazy to Parasite.
I don't think there is a one-size-fits-all answer to your question. It very much depends. But good news, ORM is flexible to cover any/all scenario you might have.
If Parasite is only used in HealthCheck class,which can be seen as an association.
Association means that the existence of child class is dependent on the existence of the parent so it has no independent lifecycle ,thus you can either declare the attributes directly in HealthCheck as you did in your second example ,or you can declare them in Parasite class and then make it Embeddable inside the HealthCheck class,e.g:
/*To embed a class inside in Entity you must declare it Embeddable via the JPA
annotation #Embeddable */
#Embeddable
public class Parasite {
#Column(name="body_location")
private BodyLocation bodyLocation;
#Column(name="coverage")
private Coverage coverage;
}
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#Embedded
private Parasite parasite;
}
Here your HealthCheck db table will have the attributes specified in the Parasite class,and note that Parasite table won't be created since it is Embedded and not an Entity (#Entity).
Hope this helps!
I have an indexed entity, like below :
#MappedSuperclass
public static class Model{
#Id
#GeneratedValue(strategy=GenerationType.Identity)
private Integer id;
private boolean isDeleted;
}
and the indexed class is :
#Entity
#Table("USERS")
#Indexed
public class ProductModel extends Model{
#Field
private String name;
//getters & setters
}
Well, when I do a research on ProductModel, I get the value of the flag isDeleted while its not annotated with #Field.
I'm asking if this is a normal behavior, does Hibernate-search Index the whole Object Or does it fetch the missing data from data base, I need an explanation for this behavior please.
Hibernate Search only stores in the index the fields you declare explicitly (more precisely, it indexes by default and you can ask Hibernate Search to store it by adding the store option to your #Field annotation).
What you observe is that Hibernate Search hydrates the objects with the information of the database after having performed the search. This is one of the main interest in using Hibernate Search: the objects returned are managed entities.
I'm using spring CrudRepository throughout my application.
Now I want to also create one for an #Entity that does not have an #Id. Is that possible at all?
//probably ID is always required?
public interface Repository<T, ID extends Serializable>
JPA requires that every entity has an ID. So no, entity w/o an ID is not allowed.
Every JPA entity must have a primary key.
from JPA spec
You may want to read more about how JPA handles a case when there's no id on the DB side from here (see 'No Primary Key').
Alternatively you can extend AbstractPersistable<Long> for your all POJO entities.
Follow example: - https://github.com/spring-projects/spring-data-examples/blob/master/jpa/example/src/main/java/example/springdata/jpa/simple/User.java
Use #IdClass (composite key)
Yes, this is possible. This approach can be used when you read from a DB view. The below example demonstrates how to mark all (2, in the given example) columns as the composite ID:
import java.io.Serializable;
import lombok.Data; // auto-generates AllArgs contractor, getters/setters, equals, and hashcode
#Data
#Entity
#IdClass(MyEntity.class) // <--this is the extra annotation to add
#Table(name = "my_table")
public class MyEntity implements Serializable{ // <--this is the extra interface to add
#Id // annotate each column with #Id
private String column1;
#Id // annotate each column with #Id
private String column2;
}
The above MyEntity uses the full record state as the key:
--so, in addition to #EqualsAndHashcode, do flag the class with Serializable interface;
--annotate each field with #ID
In your <T, ID> repository say the ID is your full (entity) class:
interface MyEntityRepo extends CrudRepository<MyEntity, MyEntity> {
// ^^^the full record is the ID
See also:
docs.oracle.com - Example 7.4-7.5: Non-Embedded Composite Primary Key
baeldung.com/jpa-composite-primary-keys, for more fine-grained custom IdClass, with fewer (than all) columns in the ID.
I have the following JPA entities.
A profile have many users and a user have many profiles:
#Entity
public class Profile implements Serializable {
#Id
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
private List<User> users;
...
}
#Entity
public class User implements Serializable {
#Id
private Long id;
#ManyToMany(mappedBy = "users")
private List<Profile> profiles;
...
}
On my application, when a user is merged, the profiles are updated on database.
However, when a profile is merged, the users are not updated.
Is possible to map my entities in order to make both sides merge their lists?
I am using JPA 2.1 and Hibernate.
Your Profile entity is ownind side or relationship. It's up to it, to manage relationship, so in order to update User you'll have to update Profile too or make manual SQL calls.
Java Specification for JPA 2.1 says that:
• For many-to-many bidirectional relationships either side may be the owning side
So if you'd like to make both entities editable from both side, remove mappedBy element and assigne necessacy cascade. But I'm not sure it works in Hibernate (didn't try actually), see this docs on mapping, there's no information about m:m without owning side: http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch07.html#collections-bidirectional
Otherwise, you may need to iterate through collection in Profile entity and then change them. For example:
for( User user : profile.getUsers() ) {
user.setSomething(.....);
}
session.merge(profile);
Changing List to Set might be needed in order to avoid Hibernate's delete and reinsert, described here: http://assarconsulting.blogspot.fr/2009/08/why-hibernate-does-delete-all-then-re.html
Also, don't forget about equals() and hashCode() methods override
Given the following example (departments - projects):
A department has the following properties (composite primary key):
#Entity
#IdClass(DeptId.class)
public class Department
{
#Id
#Column(name="number")
private Integer number;
#Id
#Column(name="country")
private String country;
#Column(name="name")
private String name;
#OneToMany(mappedBy="dept")
private Collection<Project> projects;
...
}
Here the PK class:
public class DeptId implements Serializable
{
private Integer number;
private String country;
...
}
The relationship between projects and departments is many-to-one, that is a deptartment can have many projects. The Project class is itself using a composite key referencing Department's composite key. Important note: it's only about the implementation with #IdClass not #EmbeddedId.
Then the (problematic) JPA 1.0 #IdClass implementation would have to look something like that (redundant deptNum and deptCtry properties): -> it's just a unique name within a department
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number"),
#JoinColumn(name="dept_country", referencedColumnName="country")
})
private Department dept;
...
}
The ProjectId is:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId (or the properies within it). -> MappingException etc.
My question is:
Is this a limitation of JPA 1.0, that tables with composite keys referencing other composite keys with #IdClass implementations generally WON'T work, because the JPA implementation simply can't know how to map these fields?
As a workaround, you'd have to use #EmbeddedId for these classes or use JPA 2.0 syntax to annotate the #XToX associations with #Id. I just want to make sure my view on this is right.
Thanks
Yes, this is a limitation of JPA 1.0, corrected in JPA 2.0. In the new JPA 2.0, you can put the ID annotation on your dept relationship and completely avoid having the redundent deptCountry and deptNumber attributes, with the key class using nesting. In JPA 1.0, only basic mappings can be marked as apart of the ID, requiring redundent mappings and some code to ensure that the values/relationships get put into the cache correctly when persisting. Because of the redundancy, as mentioned in other answers, one of the mappings for a field needs to be marked read-only via the insertable/updatable=false. Doing so though means that value is not merged into the cache - so changes (such as on insert, since you can't change an objects ID once it exists) will not be reflected unless the object is refreshed from the database. If you mark the JoinColumns as read-only, you will need to get the values from the referenced dept and put them into the correspoinding basic id attributes manually when you want to persist a Project. But, you can also mark the basic attributes as read-only. Eclipselink anyway will not have any problems and will correctly set the field values using the associated dept entity (as long as it is set before persist is called on the Project). Notice though that the basic attributes may or may not be populated when you read back the project in a different context- this will depend on if the entity is refreshed from the database or not. If they are read-only, they do not get merged into the shared cache since they, being read only, should not have changed. So they can be just ignored, or if they must be populated, the entity refreshed or the values set from the dept in an event.
This same model can be reused by using the JPA2.0 #MapsId, which will also maintain the basic mappings using the values from the relationship for you. Only benifit I see is that you don't need to access the relationship (potentially causing unneccessary joins or database access on lazy relationships) to get the foreign key/id field values.
As for the ZipArea EclipseLink exceptions, they are due to ZipAreaId having a ZipId zip attribute instead it being flattened out. JPA 1.0 requires the key class to have an attribute of the same type and name for each #ID attribute in the Entity.
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId
This is why you need to define the ManyToOne foreign key(s) as read-only with this kind of mapping. This is done by setting the JoinColumn attributes insertable and updatable to false.
So try the following:
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number", insertable=false, updatable=false),
#JoinColumn(name="dept_country", referencedColumnName="country", insertable=false, updatable=false)
})
private Department dept;
...
}
The problem with the posted code is, that JPA 1.0 really doesn't allow nesting of composite primary key classes. This ProjectId is invalid:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
DeptId has to be flattened, like:
public class ProjectId implements Serializable
{
private Integer deptNumber;
private String deptCountry;
private String name;
...
}
I just got an EclipseLink version to go, but Hibernate has problems with that. I wonder how to tell Hibernate that JPA 1.0 is assumed.