I am currently using 2 classes that have a OneToMany relation. One class contains catalogs (you can think of it as book); an other class contains template (you can think of it as pages). In this scenario, one template can belong only to one catalog hence I used the OneToMany relation.
My application goes very well until I restart the service. It is currently running on Hana Cloud Platform under MaxDB. I am using JPA and eclipselink (I used #AdditionalCriteria to manage my multi-tenancy as the multi-tenancy offered by JPA does not allow me to make queries on multiple tenants).
Here is an extract of my code for the Catalog:
#Entity
#Table(name = "Catalog")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Catalog implements Serializable {
private static final long serialVersionUID = -3906948030586841482L;
#Id
#GeneratedValue
private long id;
[...]
#OneToMany(cascade = ALL, orphanRemoval = false, fetch = EAGER, mappedBy = "catalog")
private Set<Template> templates = new HashSet<Template>();
[...]
public void setTemplate(Template template) {
this.templates.add(template);
}
}
The code for Template is the following:
#Entity
#Table(name = "Template")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Template implements Serializable {
private static final long serialVersionUID = 5268250318899275624L;
#Id
#GeneratedValue
private long id;
[...]
#ManyToOne(cascade = ALL, fetch = EAGER)
#JoinColumn(name = "catalog_id", referencedColumnName = "id")
private Catalog catalog;
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
if(!catalog.getTemplate().contains(this))
catalog.getTemplate().add(this);
}
}
In my Servlet, I use only the Catalog to make operations. If I have to save a template, I read it from the catalog, make the modifications in the template and persist the catalog.
It works very well until I restart my service.
The catalog does not have any references to the templates anymore BUT the template still have a reference to the catalog it used to belong to.
Can you please point me into the right direction?
Thanks
Related
I have a Spring Boot application using Hibernate as JPA provider. My application has two entities connected with a #OneToMany / #ManyToOne relation. The relation is annotated with #Fetch(FetchMode.JOIN) on both directions, and fetch = FetchType.EAGER.
My entities are called Car and Driver:
#Entity
#Table(name = "car")
#Data
public class Car implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "car_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "car_seq") })
#GeneratedValue(generator = "car_seq")
private Integer id;
#OneToMany(mappedBy = "car", fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private List<Driver> drivers = new ArrayList<>();
#Column(name = "license_no", nullable = false)
private String licenseNo;
}
#Entity
#Table(name = "driver")
#Data
public class Driver implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "driver_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "driver_seq") })
#GeneratedValue(generator = "driver_seq")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "car_id", nullable = true)
#Fetch(FetchMode.JOIN)
private Car car;
#Column(name = "name", nullable = false)
private String name;
}
When selecting a care (e.g. by calling Car.findById()), Hibernate joins the the two tables in a single SQL, and returns a Car object with a list of Drivers.
But if I select a single driver, Hibernate will join the Driver and Car table to give me the Driver object with the Car property populated, but it will run a second query to fetch all the driver objects on for the list on the car object.
For performance reasons I would like all the involved objects to be fetched in a single query, as is the case when I fetch a car. But I cannot find a way to make Hibernate do this. There is a property, hibernate.max_fetch_depth, which is supposed to do this, but I have found that it only affects the behavior of fetching a car, not when I fetch a driver.
I know I can use an EntityGraph to control the fetching, and by using an EntityGraph I have successfully retrieved a driver object with its car and all the car's drivers in one query. But to do that, I have to explicitly use a graph when retrieving the object, and I cannot do that in all the various cases where a Car object is needed. There are lots of other entities that have a relation to Car, and I don't want to write an EntityGraph for each and every one of those.
So is there a way to tell Hibernate how you want the fetching to be done by default on an entity? I would have thought that the annotations would be enough, but it seems that there either has to be something more, or that this simply cannot be done.
Arndt
FetchType.EAGER is one of the most common reasons for performance problems. You should use
#OneToMany(mappedBy = "car")
private List<Driver> drivers = new ArrayList<>();
And fetch drivers If needed
SELECT c FROM Car c JOIN FETCH c.drivers
I'm trying to implement entity auditing in my Java Spring Boot project using spring-data-envers. All the entities are being created as they should, but I've come up against a brick wall when executing the query.
parentRepository.findRevisions(id).stream().map(Parent::getEntity).collect(Collectors.toList());
During this select the repository is supposed to fetch info also from the child entity, instead I get unable to find <child object> with {id}.
According to my experiments categoryId is being searched in the Category_Aud table, instead of the actual table with desired data.
Code snippets:
#Data
#Entity
#Audited
#NoArgsConstructor
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private Status status;
#Enumerated(EnumType.STRING)
private Type requestType;
private String fullName;
#ManyToOne
#JoinColumn(name = "child_id")
private Child child;
}
#Data
#Entity
#Audited
#NoArgsConstructor
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
}
I've extended Parent with RevisionRepository
#Repository
public interface ParentRepository extends RevisionRepository<Parent, Long, Long>, JpaRepository<Parent, Long>
And annotated my SpringBootApplication entry class with:
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
I couldn't find any explanation for this so far, how can make parentRepository get what I need?
The underlying problem here is that the reference from a versioned entity isn't really properly defined. Which variant of the reference should be returned? The one at the start of the version you use as a basis, the one at the end? The one that exists right now?
There are scenarios for which each variant makes sense.
Therefor you have to query the revisions yourself and can't simply navigate to them.
It's my first post, so I hope I do it the right way. I have searched two days for an equivalent Problem, but did not find anything.
Here is what I did:
We have an Entity, that contains (beside others) the folowing fields:
#Entity
#Access(AccessType.FIELD)
#Table(name = "component")
public class Component {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
#OneToMany
#JoinTable(name = "component_dokumentation",
joinColumns = #JoinColumn( name = "component_id" ),
inverseJoinColumns = #JoinColumn(name = "dokumentation_id"))
private Set<FileType> dokumentation;
private Long keySisMf = 0L;
.
.
// Getter and Setter and stuff
}
After one year of usage we have found out, that our Entity became too big and that we have to use DTO Objects to transfer data to the Client, modify them and return them to the Server. For this purpose we modelled an embeddable Entity ComponentAttributes.
So right now it Looks like:
#Entity
#Access(AccessType.FIELD)
#Table(name = "component")
public class Component {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
#Embedded
private ComponentAttributes componentAttributes;
.
.
}
#Embeddable
#Access(AccessType.FIELD)
public class ComponentAttributes {
private static final long serialVersionUID = 1L;
#OneToMany
#JoinTable(name = "component_dokumentation",
joinColumns = #JoinColumn( name = "component_id" ),
inverseJoinColumns = #JoinColumn(name = "dokumentation_id"))
private Set<FileType> dokumentation;
private Long keySisMf = 0L;
.
.
// Getter and Setter and stuff
}
We did not change anything in the Database. We have encountered Problems in setting values for the set documentation. The field keySisMf is not a Problem. The Problems are just related to the documentation (I must add that FileType is just a Basic Entity consisting of an id and several Strings, so nothing Special). Getting the values and transfering them to the Client is fast and correct. Telling the Server to Change keySisMf is not a Problem. Telling the Server to add or remove a FileType instance simply does not work. No Errors but no changes.
We have logged the JPA generated SQL and there is no SQL generated for component.getComponentAttributes().setDokumentation(fileSet).
We use a Glassfish 4.1.1 Server with an ORACLE Database. Did I miss something when moving dokumentation from Component to ComponentAttributes????
Thanks for your help and patience.
Chris
I have theses entity and I do this query.
select r from RentAmount r Join r.lodger l join l.bailList b where r.unpaidBalance > 0 and (r.paymentDueDate > :date or r.paymentDueDate is null ) and b.paymentPeriod= order by r.rentAmountId")
Is there a way to feed Lodger.bailList only with the last bail or i would need to loop on every record to get this information?
#Entity
public class RentAmount {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long rentAmountId;
#OneToOne
private Lodger lodger;
}
#Entity
public class Lodger{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long lodgerId;
#OneToOne(fetch = FetchType.LAZY, mappedBy="lodger")
private RentAmount rentAmount;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY, mappedBy = "lodger", orphanRemoval = true)
private List<Bail> bailList;
}
#Entity
public class Bail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long bailId;
#Enumerated(EnumType.STRING)
private PaymentPeriodEnum paymentPeriod;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lodger_id")
private Lodger lodger;
}
There are a few options:
One (Non JPA, Hibernate Only)
Ensure the collection is correctly ordered and mark it is as extra lazy. You will have access to the whole collection but accessing of individual items will not trigger a full load.
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html
"Extra-lazy" collection fetching: individual elements of the
collection are accessed from the database as needed. Hibernate tries
not to fetch the whole collection into memory unless absolutely
needed. It is suitable for large collections.
The mapping will look like:
#OneToMany(mappedBy = "lodger")
#LazyCollection(LazyCollectionOption.EXTRA)
#OrderBy("theRelevantProperty ASC")
private List<Bail> bailList;
public void getCurrentBail(){
//will only load this item from the database
return bailList.get(bailList.size() - 1);
}
Two (Non JPA, Hibernate Only.)
Use the #Where annotation to filter the collection so that while still #OneToMany, only one element will be accessible.
https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-collection
The mapping will look like:
#OneToMany(mappedBy = "lodger")
#Where(clause="some native sql which will filter to include onyl 1item"))
private List<Bail> bailList;
public void getCurrentBail(){
//will be the only item accessible
return bailList.get(0);
}
Three (JPA Compliant)
Would involve creating views at the database level. Various options in this area. If we are only ever interested in the current bail then this view would be similar to option 2 above. Simply point the Bail entity to this view rather than the concrete table:
#Entity
#Table(name = "vw_active_bail")
public class Bail {
}
Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?
You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.
Make the eager object Serializable