Suppose I have an entity "Parent" which holds a list of "Child" objects.
In Java this looks like this:
public class ParentEntity implements Parent {
protected int id;
#Override
public int getId() { return id; }
#Override
public void setId(int id) { this.id = id; }
protected List<Child> children;
#Override
public List<Child> getChildren() { return children; }
#Override
public void setChildren(List<Child> children) { this.children = children; }
#Override
public void save() {
// Do some Hibernate "save" magic here...
}
public static Parent getById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
session.close();
return entity;
}
}
My business logic class shall only work with the interface class, like this:
public class BusinessLogic {
public void doSomething() {
Parent parent = ParentEntity.getById(1);
for (Child c : parent.getChildren())
System.out.println("I love my daddy.");
}
}
Unfortunately, this doesn't work because the parent's children do not get loaded and the loop crashes with a NullPointerException.
1. Approach "Eager Loading"
There are two problems with this approach. Even though in the XML I wrote "lazy='false'" Hibernate seems to ignore this.
Secondly, eager loading is not desirable in my case since we could have hundreds of children, potentially.
2. Approach "Load/Initialize on 'GET'"
#Override
public List<Child> getChildren()
{
if (!Hibernate.isInitialized(children)) {
Session session = HibernateUtil.getSessionFactory().openSession();
Hibernate.initialize(children);
session.close();
}
return children;
}
This doesn't work either because I get an exception saying that the collection is not linked to a session. The session which was used to load the parent entity was closed, previously.
What do you suggest is the 'best practice' solution here? I really don't want to mess with Hibernate sessions in my business logic.
Either you can use a query-object or a custom query and fetch all children in the scenarios you really need them (search for left join fetch), for a few thousand objects this might work.
However if the amount of records could reach millions you are running most likely into memory issues, then you should think about a shared cache, retrieving the data on a page by page basis Just search for n+1 and hibernate you will find plenty of discussions around this topic.
The simplest hack I can think of:
public static Parent getParentWithChildrenById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
Hibernate.initialize(entity.children);
session.close();
return entity;
}
Side-note: Having the data access logic in you domain layer is considered bad practice.
I've always allowed Spring and JPA to manage my Hibernate sessions, so most of the painful boilerplate code disappears at that point. But you still have to call entity.getChildren().size() (or something similar) before you exit the data layer call where the session is opened, to force Hibernate to do the retrieval while there's still a session to use.
Related
I've recently had to implement a cache invalidation system, and ended up hesitating between several ways of doing it.
I'd like to know what the best practice is in my case. I have a classic java back-end, with entities, services and repositories.
Let's say I have a Person object, with the usual setters and getters, persisted in a database.
public class Person {
private Long id;
private String firstName;
private String lastName;
...
}
A PersonRepository, instance of JpaRepository<Person, Long>.
public class PersonRepository extends JpaRepository<Person, Long> {
public Person save(Person person) {return super.save(person);}
public Person find(Person person) {return super.find(person);}
public void delete(Person person) {super.delete(person);}
}
I have a PersonService, with the usual save(), find(), delete() methods and other more functional methods.
public class PersonService {
public Person save(Person person) {
doSomeValidation(person)
return personRepository.save(person);
}
...
}
Now I also have so jobs that run periodically and manipulate the Person objects. One of which is running every second and uses a cache of Person objects, that needs to be rebuilt only if the firstName attribute of a Person has been modified elsewhere in the application.
public class EverySecondPersonJob {
private List<Person> cache;
private boolean cacheValid;
public void invalidateCache() {
cacheValid = false;
}
public void execute() { // run every second
if (!cacheValid)
cache = buildCache();
doStuff(cache);
}
}
There are lots of places in the code that manipulate Person objects and persist them, some may change the firstName attribute, requiring an invalidation of the cache, some change other things, not requiring it, for example:
public class ServiceA {
public void doStuffA(Person person) {
doStuff();
person.setFirstName("aaa");
personRepository.save(person);
}
public void doStuffB(Person person) {
doStuff();
person.setLastName("aaa");
personService.save(person);
}
}
What is the best way of invaliding the cache?
First idea:
Create a PersonService.saveAndInvalidateCache() method then check every method that calls personService.save(), see if they modify an attribute, and if yes, make it call PersonService.saveAndInvalidateCache() instead:
public class PersonService {
public Person save(Person person) {
doSomeValidation(person)
return personRepository.save(person);
}
public Person saveAndInvalidateCache(Person person) {
doSomeValidation(person)
Person saved = personRepository.save(person);
everySecondPersonJob.invalidateCache();
return saved;
}
...
}
public class ServiceA {
public class doStuffA(Person person) {
doStuff();
person.setFirstName("aaa");
personService.saveAndInvalidateCache(person);
}
public class doStuffB(Person person) {
doStuff();
person.setLastName("aaa");
personService.save(person);
}
}
It requires lots of modifications and makes it error prone if doStuffX() are modified or added. Every doStuffX() has to be aware if they must invalidate or not the cache of an entirely unrelated job.
Second idea:
Modify the setFirstName() to track the state of th ePerson object, and make PersonService.save() handle the cache invalidation:
public class Person {
private Long id;
private String firstName;
private String lastName;
private boolean mustInvalidateCache;
setFirstName(String firstName) {
this.firstName = firstName;
this.mustInvalidateCache = true;
}
...
}
public class PersonService {
public Person save(Person person) {
doSomeValidation(person);
Person saved = personRepository.save(person);
if (person.isMustInvalidateCache)
everySecondPersonJob.invalidateCache();
}
...
}
That solution makes it less error prone by not making every doStuffX() need to be aware of if they must invalidate the cache or not, but it makes the setter do more than just change the attribute, which seems to be a big nono.
Which solution is the best practice and why?
Thanks in advance.
Clarification: My job running every second calls, if the cache is invalid, a method that retrieves the Person objects from the database, builds a cache of other objects based upon the properties of the Person objects (here, firstName), and doesn't modify the Person.
The job then uses that cache of other objects for its job, and doesn't persist anything in the database either, so there is no potential consistency issue.
1) You don't
In the usage scenario you described the best practice is not to do any self grown caching but use the cache inside the JPA implementation. A lot of JPA implementations provide that (e.g. Hibernate, EclipseLink, Datanucleus, Apache OpenJPA).
Now I also have so jobs that run periodically and manipulate the Person objects
You would never manipulate a cached object. To manipulate, you need a session/transaction context and the database JPA implementation makes sure that you have the current object.
If you do "invalidation", as you described, you loose transactional properties and get inconsistencies. What happens if a transaction fails and you updated the cache with the new value already? But if you update the cache after the transaction went through, concurrent jobs read the old value.
2) Different Usage Scenario with Eventual Consistent View
You could do caching "on top" of your data storage layer, that provides an eventual consistent view. But you cannot write data back into the same object.
JPA always updates (and caches) the complete object.
Maybe you can store the data that your "doStuff" code derives in another entity?
If this is a possibility, then you have several options. I would "wire in" the cache invalidation via JPA triggers or the "Change Data Capture" capabilities of the database. JPA triggers are similar to your second idea, except that you don't need that all code is using your PersonService. If you run the tigger inside the application, your application cannot have multiple instances, so I would prefer getting change events from the database. You should reread everything from time to time in case you miss an event.
I am struggeling with a design - persistence issue . I have a tree and all elements in this tree are subclasses of the same abstract superclass. The tree has a root object with a list of children which themselves can have children, and so on. Changes to attributes of a children would need to be propagated to all parent levels. To make it easier to illustrate, let's say we deal with products which would have multiple releases, having multiple development phases. Each of these would have a lifetime. So we would have something like
#Entity
#Access(AccessType.PROPERTY) // JPA reading and writing attributes through their getter and setter methods
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
name = "lifetime_level",
length=10
)
public abstract class LifeTime implements Serializable {
// all classes are incomplete for sake of brevity
protected Integer version; // Optimistic locking support
protected LocalDate plannedBegin; // The calculated, potentially shifted, begin resulting from children assignments
protected LocalDate plannedEnd;
private LifeTime parent;
protected List<LifeTime> children;
#ManyToOne
public LifeTime getParent() {
return parent;
}
public void setParent(LifeTime parent) {
this.parent = parent;
}
#OneToMany(mappedBy="parent")
public List<LifeTime> getChildren() {
return children;
}
private void setChildren(List<LifeTime> children) {
this.children = FXCollections.observableList(children);
}
public void addChild(LifeTime child) {
children.add(child);
}
public LocalDate getPlannedBegin() {
return plannedBegin;
}
public void setPlannedBegin(LocalDate aBegin) {
this.plannedBegin = aBegin;
adjustParentBegin(parent, this);
}
public LocalDate getPlannedEnd() {
return plannedEnd;
}
public void setPlannedEnd(LocalDate aEnd) {
this.plannedEnd = aEnd;
adjustParentEnd(parent, this);
}
protected void adjustParentBegin(LifeTime parent, LifeTime child) {
if(child.getPlannedBegin.isBefore(parent.getPlannedBegin)) {
parent.setPlannedBegin(child.getPlannedBegin);
}
}
protected void adjustParentEnd(LifeTime parent, LifeTime child) {
if(child.getPlannedEnd.isAfter(parent.getPlannedEnd)) {
parent.setPlannedEnd(child.getPlannedEnd);
}
}
#Version
public Integer getVersion() {
return version;
}
private void setVersion(Integer version) {
this.version = version;
}
}
We would have the concrete classes Product, Release and Phase. All would extend LifeTime. As root object we have a product. The product has children representing releases of the product and each release would have several development phases. We would also have baselines, iterations that we ignore for the moment. Next, we have somewhere a service class that cares for handling the business logic:
#Service
public class LifeTimeService {
#Autowired ProductRepository productRepository;
#Autowired ReleaseRepository releaseRepository;
#Autowired PhaseRepository phaseRepository;
public Product createProduct() {
Product product = new Product();
release.setPlannedBegin(LocalDate.now());
release.setPlannedEnd(LocalDate.now());
product = productRepository.save(product); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving release
createRelease(product);
return productRepository.save(product);
}
public Release createRelease(Product product) {
Release release = new Release();
product.addChild(release);
release.setParent(product);
release.setPlannedBegin(product.getPlannedBegin()); // initial values
release.setPlannedEnd(product.getPlannedEnd());
release = releaseRepository.save(release); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving phases
addPhases(release); // add phases and adjust begin and end of the release
return releaseRepository.save(release);
}
protected void addPhases(Release release) {
LocalDate date = release.getPlannedBegin;
Phase phase1 = new Phase();
release.addChild(phase1);
phase1.setParent(release);
phase1.setPlannedBegin(date);
date = date.plusMonth(3);
phase1.setPlannedEnd(date);
phaseRepository.save(phase1);
phase2 = new Phase();
release.addChild(phase2);
phase2.setParent(release);
phase2.setPlannedBegin(date);
date = date.plusMonth(3);
phase2.setPlannedEnd(date);
phaseRepository.save(phase2);
}
}
Let's say we have Controller class, that makes use of the Service
#Controller
public class MyController {
#Autowired LifeTimeService service;
protected Product product;
public void myTest() {
Product product = service.createProduct(); // this creates a product with an initial release and all phases
Release release = service.createRelease(product); // now the product has a later plannedEnd than the corresponding database row
}
}
Obviously, I want that the createRelease method creates and returns a release. The issue is that it alters all levels of the tree: It creates phases but also changes the parent product begin and end date. I would need to save the product after I call createRelease to persist this change. This approach doesn't scale if the tree has more levels. Otherwise, if I save the product within createRelease, the product in the myTest method would have the wrong version. Or createRelease returns the saved parent - what is counter intuitive - and I have to code a method which return last created release. This is all unsatisfying.
While the above class example follows the Domain Driven Design, whereby each object is in charge of securing its integrity, I was although thinking about Anemic Domain Model and moving the two adjustment methods to the service class and make them recursive. But I don't see how it changes or fixes the above issue.
In reality my tree goes over at least 5 hierarchical levels. So whatever solution you propose, it should scale to multiple levels.
I'm new to using JPA and trying to transition my code from JdbcTemplate to JPA. Originally I updated a subset of my columns by taking in a map of the columns with their values and created the SQL Update string myself and executed it using a DAO. I was wondering what would be the best way to do something similar using JPA?
EDIT:
How would I transform this code from my DAO to something equivalent in JPA?
public void updateFields(String userId, Map<String, String> fields) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : fields.entrySet()) {
sb.append(entry.getKey());
sb.append("='");
sb.append(StringEscapeUtils.escapeEcmaScript(entry.getValue()));
sb.append("', ");
}
String str = sb.toString();
if (str.length() > 2) {
str = str.substring(0, str.length() - 2); // remove ", "
String sql = "UPDATE users_table SET " + str + " WHERE user_id=?";
jdbcTemplate.update(sql, new Object[] { userId },
new int[] { Types.VARCHAR });
}
}
You have to read more about JPA for sure :)
Once entity is in Persistence Context it is tracked by JPA provider till the end of persistence context life or until EntityManager#detach() method is called. When transaction finishes (commit) - the state of managed entities in persistence context is synchronized with database and all changes are made.
If your entity is new, you can simply put it in the persistece context by invoking EntityManager#persist() method.
In your case (update of existing entity), you have to get a row from database and somehow change it to entity. It can be done in many ways, but the simpliest is to call EntityManager#find() method which will return managed entity. Returned object will be also put to current persistence context, so if there is an active transaction, you can change whatever property you like (not the primary key) and just finish transaction by invoking commit (or if this is container managed transaction just finish method).
update
After your comment I can see your point. I think you should redesign your app to fit JPA standards and capabilities. Anyway - if you already have a map of pairs <Attribute_name, Attrbute_value>, you can make use of something called Metamodel. Simple usage is shown below. This is naive implementation and works good only with basic attributes, you should take care of relationships etc. (access to more informations about attributes can be done via methods attr.getJavaType() or attr.getPersistentAttributeType())
Metamodel meta = entityManager.getMetamodel();
EntityType<User> user_ = meta.entity(User.class);
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate<User> update = cb.createCriteriaUpdate(User.class);
Root e = update.from(User.class);
for( Attribute<? super User, ?> attr : user_.getAttributes() ) {
if (map.containsKey(attr.getName())) {
update.set(attr, map.get(attr));
}
}
update.where(cb.equal(e.get("id"), idOfUser));
entityManager.createQuery(update).executeUpdate();
Please note that Update Criteria Queries are available in JPA since 2.1 version.
Here you can find more informations about metamodel generation.
Alternatively to metamodel you can just use java reflection mechanisms.
JPA handles the update. Retrieve a dataset as entity using the entitymanager, change the value and call persist. This will store the changed data in your db.
In case you are using Hibernate(as JPA provider), here's an example
Entity
#Entity
#Table(name="PERSON")
public class Person {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="NAME", nullable=false)
private String name;
other fields....
}
DAO
public interface PersonDao {
Person findById(int id);
void persist(Person person);
...
}
DaoImpl
#Repository("personDao")
public class PersonDaoImpl extends AnAbstractClassWithSessionFactory implements PersonDao {
public Person findById(int id) {
return (Person) getSession().get(Person.class, id);
}
public void persist(Person person){
getSession().persist(person);
}
}
Service
#Service("personService")
#Transactional
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Override
public void createAndPersist(SomeSourceObject object) {
//create Person object and populates with the source object
Person person = new Person();
person.name = object.name;
...
personDao.persist(person);
}
#Override
public Person findById(int id) {
return personDao.findById(id);
}
public void doSomethingWithPerson(Person person) {
person.setName(person.getName()+" HELLO ");
//here since we are in transaction, no need to explicitly call update/merge
//it will be updated in db as soon as the methods completed successfully
//OR
//changes will be undone if transaction failed/rolledback
}
}
JPA documentation are indeed good resource for details.
From design point of view, if you have web interfacing, i tends to say include one more service delegate layer(PersonDelegateService e.g.) which maps the actual data received from UI to person entity (and viceversa, for display, to populate the view object from person entity) and delegate to service for actual person entity processing.
We are using spring-data-neo4j release 2.2.2.Release and Neo4j 1.9
Saving and updating nodes (properties) works fine using a GraphRepository
Our most simple example looks like this:
public interface LastReadMediaRepository extends GraphRepository<Neo4jLastReadMedia> {}
We also set some relationships connected to a node, the node class looks like this
#NodeEntity
public class Neo4jLastReadMedia {
#GraphId
Long id;
#JsonIgnore
#Fetch #RelatedToVia(type = "read", direction = Direction.OUTGOING)
Set<LastReadMediaToLicense> licenseReferences;
public Neo4jLastReadMedia() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void read(final Neo4jLicense license, final Long lastAccess, final float progress, final Long chapterId) {
licenseReferences.add(new LastReadMediaToLicense(this, license, lastAccess, progress, chapterId));
}
public Set<LastReadMediaToLicense> getLicenseReferences() {
return licenseReferences;
}
#Override
public String toString() {
return "Neo4jLastReadMedia [id=" + id + "]";
}
}
Now, we save a node using the repository's save() method. The relationships are saved, too, at least for the first save.
Later when we want to change properties on a relationship (update a relationship) that already exists (e.g. lastAccess) we retrieve the node from the database, manipulate its relationship Set, here Set<LastReadMediaToLicense> licenseReferences; and then, try to save the node back with save()
Unfortunately, the relationship is not updated and all properties remain the same...
We know how to do that using annotated cypher queries in the repo, but there has to be an "abstracted" way?!
Thanks a lot!
Regards
EDIT: If I remove a relationship from the Set, then perform a save() on the node, the relationship is deleted. Only update does not work! Or is that the intention?
Andy,
SDN only checks for modifications of the set, aka additions and removals, it doesn't check each of the relationships for a change, that would be even more costly.
Usually that can be solved by saving the relationship via a repository or template instead of adding it to a set and then saving the node. That is also faster.
Before asking the question, here is the context of my problem:
I got a specific entity named 'Category', which have a property Category (a parent) and another property of type 'relatedEntity'.
Here is a sample of the code:
#Entity
public class Category {
...
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityID")
private RelatedEntity entity;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="parentID")
private Category parent;
...
}
So, as you can see, I've declared ManyToOne relationships for these properties, and also a lazy loading fetching strategy.
Now, I use this method inside a DAO object to get a list of Category:
public List<Category> getAll() {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(Category.class);
return (crit.list());
}
I got a specific service object which calls the DAO method:
#Transactional(readOnly = true)
public List<Category> getAllCategories() {
return (categoryDAO.getAll());
}
Nothing really extraordinary for the moment... Now, here come my unit tests:
#Test
public void testCategories() {
List<Category> cat = service.getAllCategories();
assertNotNull(cat);
assertFalse(cat.isEmpty());
for (Category c : cat) {
try {
assertNotNull(c.getEntity().getName());
fail("Expected lazy init. on Category.Entity");
}
catch(LazyInitializationException ex) {
//Exception is caught
}
try {
assertNotNull(c.getParent().getName());
//No exception
fail("Expected lazy init. on Category.Parent");
}
catch(LazyInitializationException ex) {}
}
}
After running the test, an exception is catched in the first try/catch block and the second fail is fired, saying that there is no LazyInitException on the parent property of the category.
So I can call
c.getParent().getParent().getName();
or
c.getParent().getParent().getParent().getName()
without any exception being raised.
I've set 'show_sql=true' in my configuration file, I can't see any supplementary request in my logs after calling the getter on the parent property, so there is no new request but the parent property is loaded.
Can somebody explain me why the parent property is not lazy?
EDIT:
I've updated my test case.
#Test(expected=LazyInitializationException.class)
public void lazyCategoriesParentsList() {
List<Category> cat = service.getAllCategories();
assertNotNull(cat);
assertFalse(cat.isEmpty());
for (Category c : cat) {
assertNotNull(c.getParent().getName());
}
}
And now the test is passing... Was it related to the access of the entity property (via c.getEntity()) in my previous test case?
Could you try getting only one Category instead of loading them all ?
Indeed, when you are loading a Category, if hibernate can find its parent in the same Session, it will initialize it, even if parent is marked as lazy. As you are querying for all categories, it will find all the parents in the Session and initialize them.