For migrating from eclipse link to hibernate, I am looking for the equivalent of eclipse link annotation #AdditionalCriteria in Hibernate at #MappedSupperClass BaseEntity level, to filter logically deleted records from all entities extending this BaseEntity.
I had found #Where annotation. But, this only works at Entity level and not at BaseEntity. Please let me know if there is any possibility to add this or any other Hibernate annotation to filter BaseEntity.
#MappedSuperclass
#Where(clause = "DEL_IND = 0") // DOES NOT WORK
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "DEL_IND")
private boolean deleted = Boolean.FALSE;
public boolean getDeleted() {
return deleted;
}
public void setDeleted() {
this.deleted = Boolean.TRUE;
}
}
#Entity
#Table(name = "PERSON")
#Where(clause = "DEL_IND = 0") // THIS WORKS BUT NEEDS TO BE REPEATED IN ALL ENTITIES
public class Person extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "PERSON_ID")
private Integer id;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "FIRST_NAME")
private String firstName;
--------------------
getters & setters
--------------------
--------------------
}
You can open a Hibernate JIRA issue for this. The only workaround is to manually add the #Where annotation to all your entities or use filters.
With filters you have the option of dynamically enable/disable them, which is useful since you might want to fetch a deleted item sometimes.
Related
Let's say I have the following entities in my application:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
}
Question 1:
I want to load a SomeEntity with id = 1 for example, but I only want to load the anotherEntities partially, for example I only want the last 10 versions of it, what is the easiest and most straight forward way of doing it (with Hibernate/Spring Data JPA) with one request?
Question 2:
I want to update the previously mentioned object and add a new AnotherEntity to the list, but the save(T t) method of JpaRepository saves the whole object and I lose the ones that weren't loaded. How can I save the object so that the version will be updated by Spring Data (Optimistic Locking) and the SomeEntity won't lose previous data?
Update 1:
I am using Postgresql for database.
You have different options depending on your exact constraints.
You can use the #Where annotation:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Where(clause = "version < 10")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
You can use a filter:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Filter(
name="latestVersions",
condition="version < :version"
)
private List<AnotherEntity> anotherEntities = new ArrayList<>();
}
You can enable filters before running a query with the session:
entityManager
.unwrap(Session.class)
.enableFilter("latestVersions")
.setParameter("version", 10);
List<Account> accounts = entityManager.createQuery(
"from SomeEntity se where se.id = 1", SomeEntity.class)
.getResultList();
You can map the association as bidirectional (or as a many-to-one)
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany(mappedBy = "someEntity")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
#ManyToOne
private SomeEntity someEntity;
}
Now you can get the list of entities using an HQL query:
from AnotherEntity ae where ae.someEntity.id = 1 and ae.version < 10
When you want to create a new AnotherEntity, you can get SomeEntity from any element in the result list, or you can use EntityManager#getReference (and avoid running the query):
AnotherEntity ae = new AnotherEntity(...);
ae.setSomeEntity(em.getReference(SomeEntity.class, 1));
em.persist(ae);
The association is lazy, so Hibernate is not going to load the whole collection (unless you need to access it).
I am trying to save a JPA entity which has ManytoMany Relationship (Consumer and Product table) and OnetoOne relation with ConsumerDetailstable.Below are my entities
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity
public class Consumer {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
#JsonManagedReference
#OnToMany(mappedBy = "consumer")
private Set<ConsumerProduct> consumerProducts;
#OneToOne
private CustomerDetails consumerDetails;
}
#Entity
public class Product {
#Id
#GeneratedValue
private Long productId;
private String productCode;
#OneToMany(mappedBy = "product")
private Set<ConsumerProduct> consumerProducts;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity(the join table)
public class ConsumerProduct {
#EmbeddedId
ConsumerProductKey id;
#JsonBackReference
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Consumer consumer;
#ManyToOne
#MapsId("productId")
#JoinColumn(name = "product_id")
private Product product;
}
#Embeddable (forgein keys combined as embeded id)
public class ConsumerProductKey implements Serializable {
#Column(name="id")
private Long id;
#Column(name = "product_id")
private Long productId;
}
#Enitity (one to one relation table)
public class CustomerDetails {
#Id
#GeneratedValue
private Long consumerDtlId;
#OneToOne
private Consumer consumer;
private String city;
private String state;
private String country;
}
To save the entity am have just extended JPARepository and called save method
public class ConsumerRepository<Consumer> Implements JPARepository<Consumer, Long> {
#Override
public Consumer S save(Consumer entity) {
return save(entity);
};
}
I get java.lang.StackOverFlowError at save method.
Anything wrong with my Mappings ?
Question: Since this will be save operation and since Consumer Id is yet to be generated how do I assign to below Entities
ConsumerProduct.ConsumerProductKey (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
CustomerDetails (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
EDIT: I have updated the entity with JsonManagedReference and JsonBackedReference but still i have am facing stackoverflow error
It is due to Consumer trying to access ConsumerProduct and ConsumerProduct trying to access consumer entity and end up with StackOverflow error.
You should use #JsonManagedReference and #JsonBackReference annotation in consumer and ConsumerProduct respectivly.
I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository
Write some usual tests for my MVC webapp and stopped at findById() testing.
My model classes:
#Entity
public class Product {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private double purchasePrice;
private double retailPrice;
private double quantity;
#ManyToOne
#JoinColumn (name = "supplier_id")
private Supplier supplier;
#ManyToOne
#JoinColumn (name = "category_id")
private Category category;
#Entity
public class Category {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private List<Product> products;
#Entity
public class Supplier {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#LazyCollection(LazyCollectionOption.FALSE)
#Cascade(org.hibernate.annotations.CascadeType.ALL)
#OneToOne
private Contact contact;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany
private List<Product> products;
And my test code:
private Product productTest;
private Category categoryTest;
private Supplier supplierTest;
#Before
public void setUp() throws Exception {
categoryTest = new Category("Test category", "", null);
supplierTest = new Supplier("Test supplier", null, null);
productTest = new Product("Test product","", 10, 20, 5, supplierTest, categoryTest);
categoryService.save(categoryTest);
supplierService.save(supplierTest);
productService.save(productTest);
}
#Test
public void findById() throws Exception {
Product retrieved = productService.findById(productTest.getId());
assertEquals(productTest, retrieved);
}
Well, assertion failed, because of difference product.category.products and product.supplier.products properties, as you can see on pic:
One product have it as null, another as {PersistentBag}.
Sure I can easy hack it by writing custom equals method (which will ignore these properties), but sure it's not the best way.
So, why these fields are different?
I'm sure solution in properly annotation of entities fields.
Two pointers :
you use #LazyCollection(LazyCollectionOption.FALSE) in your relationship fields, so fields with that annotation are dynamically loaded by your ORM when you retrieve your entity while entites created in your fixture of your unit test are created outside from your ORM and you don't value these fields.
Even if you remove #LazyCollection(LazyCollectionOption.FALSE), you may have other differences if you want to do assertEquals() with a retrieved entity and a entity created by the hand. For example, with Hibernate, your lazy List will not be null but instance of PersistentList.
So, you should perform some works to perform assertions.
You may check properties individually or you may use Reflection to assert fields and ignore comparison for null fields in the expected object.
check http://www.unitils.org/tutorial-reflectionassert.html, it may help you.
I have a Spring JPA project with 3 entities: Author, Book and Category.
I want to use Hibernate Search for indexes.
Author class is #Indexed; Book class contains a Category field annotated with #ContainedIn; Category is a very simple class.
CLASS Author
#Entity
#Table
#Indexed
public class Author extends ConcreteEntity {
private static final long serialVersionUID = 1L;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#IndexedEmbedded
private List<Book> books = new ArrayList<>();
}
CLASS Book
#Entity
#Table
public class Book extends ConcreteEntity {
private static final long serialVersionUID = 1L;
#ContainedIn
private Category category;
}
CLASS Category
#Entity
#Table
public class Category extends ConceptEntity {
private static final long serialVersionUID = 1L;
}
CLASS ConcreteEntity and ConceptEntity are similars:
#MappedSuperclass
public abstract class ConcreteEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String name;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String value;
}
#MappedSuperclass
public abstract class ConceptEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String name;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String value;
}
I've got this exception while saving a resource using Hibernate Search.
org.hibernate.search.exception.SearchException: Unable to perform work. Entity Class is not #Indexed nor hosts #ContainedIn: class test.hibernate.search.Category
I don't understand how to solve this issue.
Thanks
Book is not configured correctly. You tell Hibernate Search that Book is included in the Category index (via your #ContainedIn annotation on the category field) but your Category entity is neither marked with #Indexed nor linked to another index via #ContainedIn.
Hibernate Search is just telling you that your configuration doesn't make much sense.
Considering your model, I'm pretty sure you wanted to mark category with #IndexedEmbedded instead.