How to implement a 3 Many-to-Many relationship with Hibernate? - java

I'm not 100% sure this is only a Hibernate issue as this might be a more abstract decision but I'll give it a try.
Since the problem description is a bit lengthy I'll first state that what I'd like to do is see if I can change the implementation to something which more resembles a Best practice implementation then this.
I have 3 entities, relevant to this issue: Workstation (ws), Employee and Organization-unit(org-unit).
An Employee can belong to one org-unit.
An Org-unit can hold many employees.
A Workstation is used to display data of an Org-unit(in general), Of an Org-unit and a specific employee in it and an employee which does not belong to an org-unit.
Currently, for various reasons which were out of my control, we do not use any associations between the entities in Hibernate or via DB-Constraints but we just use Columns which logically serve as Foreign-Keys.
We currently have an additional table which has 4 columns:Id,WSId,EmployeeId,OrgUnitId.
This allows a WS to refer to an orgunit (where employeeId is null),to an employee without an org-unit (orgunitId is null) or to an employee and org-unit (where none are null).
I'd like to be able to know:
1.Given a WS, which employees is it following and which org-units and how (i.e., alone, with an employee? which?)
2.Given an employee, which WS are monitoring it.
3.Given an org-unit, which WS are monitoring it and how (i.e., alone, with an employee? which?)
This issues relates to the Presentation layer as it dictates the view will be generated BUT it is a part of the domain model as a user, will use, an interface to manipulate these monitoring mappings and so these mappings are a part of the domain model.
I'm not sure if what I have is not the least evil among options, and I would greatly appreciate comments and suggestions.
EDIT From one of the answers I think it is not clear enough that a WS can display data for many such mappings at the same time, in a mixture of the above sorts (org-unit, employee etc.)

OK, I don't know how to implement this on the database side, but here is an Entity Model that should cover the relationship you are talking about.
Edit:
This is a new version in response to your comments. Now every WorkStation has n bindings each of which can have employee or orgunit or both (use DB constraints to ensure they don't have neither).
You can also access the bindings per orgunit and per employee, which should make the above queries much easier:
#Entity
public class OrgUnit{
#OneToMany(mappedBy="orgUnit")
private Set<Binding> bindings;
}
#Entity
public class Employee{
#OneToMany(mappedBy="employee")
private Set<Binding> bindings;
}
#Entity
public class Binding{
#ManyToOne(optional = true)
private Employee employee;
#ManyToOne(optional=true)
private OrgUnit orgUnit;
#ManyToOne(optional=false)
private WorkStation owner;
}
#Entity
public class WorkStation{
#OneToMany(mappedBy="owner")
private Set<Binding> bindings;
}
Sample Client code:
public Set<WorkStation> getWorkstationsPerEmployee(final Employee employee){
final Set<WorkStation> workStations = new HashSet<WorkStation>();
for(final Binding binding : employee.getBindings()){
WorkStation workStation = binding.getOwner();
if(workStation!=null)
workStations.add(workStation);
}
return workStations;
}

Sounds like all you really need is a nullable FK on Employee to OrgUnit, and two nullable FKs on WS to both Employee and OrgUnit. To see which WS are monitoring an employee, just get all the WS with matching emp_id columns. Same with the WS monitoring an OrgUnit, possibly with the additional stipulation of emp_id being null or not (depending on if you need to handle those situations separately). No idea where "patients" fits in, you didn't give any details about that.

Related

Dealing with EntityNotFoundException

I am creating simple REST API. I want to create an object via post method. Object contains other object. When I want to post it, I am receiving EntityNotFoundException which is thrown when nested object does not exist.
Code of object that I want to post:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Book {
private String title;
#ManyToOne
private Author author;
#Id
#Column(unique = true)
private String isbn;
}
Service of this object:
#Service
#RequiredArgsConstructor
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
private final AuthorRepository authorRepository;
#Override
public Book save(Book book) {
try {
Author byId = authorRepository.getById(book.getAuthor().getId());
} catch (EntityNotFoundException e) {
authorRepository.save(book.getAuthor());
}
return bookRepository.save(book);
}
}
After using post method I get this error:
javax.persistence.EntityNotFoundException: Unable to find com.jakubkolacz.qualificationTask.domain.dao.Author with id 0
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:216) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
I thought that using try catch to check if object exist, and saving author if necessary will help but it did not.
My question is where should I add some code to solve the problem. I understand why it is happening but do not know how to resolve it. The necessary thing is that I can not create service to add authors manually, they have to be added to repo only during adding new book.
The problem is that the save operation is not being cascaded down to the author object. You should add a cascade type inside ManyToOne annotation:
#ManyToOne(cascade = CascadeType.ALL)
Exception handling in Spring
If you are specifically wondering how to handle exceptions in Spring, then I would highly recommend THIS tutorial.
Entity Creation
First I would like to point out two minor problems with your entity creation.
1)#ManyToOne : while it is not necessary, I always like to annotate a many-to-one relationship with the #JoinColumn annotation. It just acts as a simple and friendly visual reminder that (assuming your relationship is bidirectional) this side is the owner of the relationship(has the foreign key)
2)#Id : as it currently stands, the JPA provider(lets assume hibernate) assumes that you the developer are taking care of assigning a unique identifier to the id field. Granted, this is sometimes neccessary when dealing with old legacy databases. However, if you are not dealing with a legacy database, I would recommend that you delete #Column(unique = true) and the String value to replace them with:
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long isbn;
#GeneratedValue will allow the system to automatically generate a value for isnb field.
strategy = GenerationType.IDENTITY tells the underlying database to handle the uniqueness and the auto incrementation in a way specific to the relational database.
Long instead of String because it can hold a larger number of bytes.
Service implementation
I have a few things to say about the BookServiceImpl class but first, good job on implementing an interface with BookService most people forget to program to an interface.
1) Single Responsibility
You are using both BookRepository and AuthorRepository which is of course fine(If it works it works). However, moving forward you should be weary not to add too many dependencies to one class. If you do so, you are breaking the Single Responsibility principle, which makes code harder to test and refactor.
2) Try and catch
The code inside your try catch blocks is a little confusing, especially since you have not shown the Author entity class. However, I am assuming you logic goes like this: if the author does not exist, save the author. Lastly save and return the book. Now you are correct in thinking that you handle the exceptions in the catch block. However, there is quite a lot to question here and only so little code to go on.
My overall recommendations
1) Break this method up : This method is trying to do three things at once. Create one method for saving the book, one for looking for the author and one for saving the author. This will allow for greater code reuse moving forward.
2) Read up on CascadeType : Specifically PERSIST, that might help you with your issues. Also, look into a many-to-many relationship as it is not uncommon for multiple books to have multiple authors.

DDD implementation with Spring Data and JPA + Hibernate problem with identities

So I'm trying for the first time in a not so complex project to implement Domain Driven Design by separating all my code into application, domain, infrastructure and interfaces packages.
I also went with the whole separation of the JPA Entities to Domain models that will hold my business logic as rich models and used the Builder pattern to instantiate. This approach created me a headache and can't figure out if Im doing it all wrong when using JPA + ORM and Spring Data with DDD.
Process explanation
The application is a Rest API consumer (without any user interaction) that process daily through Scheduler tasks a fairly big amount of data resources and stores or updates into MySQL. Im using RestTemplate to fetch and convert the JSON responses into Domain objects and from there Im applying any business logic within the Domain itself e.g. validation, events, etc
From what I have read the aggregate root object should have an identity in their whole lifecycle and should be unique. I have used the id of the rest API object because is already something that I use to identify and track in my business domain. I have also created a property for the Technical id so when I convert Entities to Domain objects it can hold a reference for the update process.
When I need to persist the Domain to the data source (MySQL) for the first time Im converting them into Entity objects and I persist them using the save() method. So far so good.
Now when I need to update those records in the data source I first fetch them as a List of Employees from data source, convert Entity objects to Domain objects and then I fetch the list of Employees from the rest API as Domain models. Up until now I have two lists of the same Domain object types as List<Employee>. I'm iterating them using Streams and checking if an objects are not equal() between them if yes a collection of List items is created as a third list with Employee objects that need to be updated. Here I've already passed the technical Id to the domain objects in the third list of Employees so Hibernate can identify and use to update the records that are already exists.
Up to here are all fairly simple stuff until I use the saveAll() method to update the records.
Questions
I alway see Hibernate using INSERT instead of updating the list of
records. So If Im correct Hibernate session is not recognising the
objects that Im throwing into it because I have detached them when I
used the convert to domain object?
Does anyone have a better idea how can I implement this differently or fix
this problem?
Or should I stop using this approach as two different objects and continue use
them as rich Entity models?
Simple classes to explain it with code
EmployeeDO.java
#Entity
#Table(name = "employees")
public class EmployeeDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public EmployeeDO() {}
...omitted getter/setters
}
Employee.java
public class Employee {
private Long persistId;
private Long employeeId;
private String name;
private Employee() {}
...omitted getters and Builder
}
EmployeeConverter.java
public class EmployeeConverter {
public static EmployeeDO serialize(Employee employee) {
EmployeeDO target = new EmployeeDO();
if (employee.getPersistId() != null) {
target.setId(employee.getPersistId());
}
target.setName(employee.getName());
return target;
}
public static Employee deserialize(EmployeeDO employee) {
return new Country.Builder(employee.getEmployeeId)
.withPersistId(employee.getId()) //<-- Technical ID setter
.withName(employee.getName())
.build();
}
}
EmployeeRepository.java
#Component
public class EmployeeReporistoryImpl implements EmployeeRepository {
#Autowired
EmployeeJpaRepository db;
#Override
public List<Employee> findAll() {
return db.findAll().stream()
.map(employee -> EmployeeConverter.deserialize(employee))
.collect(Collectors.toList());
}
#Override
public void saveAll(List<Employee> employees) {
db.saveAll(employees.stream()
.map(employee -> EmployeeConverter.serialize(employee))
.collect(Collectors.toList()));
}
}
EmployeeJpaRepository.java
#Repository
public interface EmployeeJpaRepository extends JpaRepository<EmployeeDO, Long> {
}
I use the same approach on my project: two different models for the domain and the persistence.
First, I would suggest you to don't use the converter approach but use the Memento pattern. Your domain entity exports a memento object and it could be restored from the same object. Yes, the domain has 2 functions that aren't related to the domain (they exist just to supply a non-functional requirement), but, on the other side, you avoid to expose functions, getters and constructors that the domain business logic never use.
For the part about the persistence, I don't use JPA exactly for this reason: you have to write a lot of code to reload, update and persist the entities correctly. I write directly SQL code: I can write and test it fast, and once it works I'm sure that it does what I want. With the Memento object I can have directly what I will use in the insert/update query, and I avoid myself a lot of headaches about the JPA of handling complex tables structures.
Anyway, if you want to use JPA, the only solution is to:
load the persistence entities and transform them into domain entities
update the domain entities according to the changes that you have to do in your domain
save the domain entities, that means:
reload the persistence entities
change, or create if there're new ones, them with the changes that you get from the updated domain entities
save the persistence entities
I've tried a mixed solution, where the domain entities are extended by the persistence ones (a bit complex to do). A lot of care should be took to avoid that domain model should adapts to the restrictions of JPA that come from the persistence model.
Here there's an interesting reading about the splitting of the two models.
Finally, my suggestion is to think how complex the domain is and use the simplest solution for the problem:
is it big and with a lot of complex behaviours? Is expected that it will grow up in a big one? Use two models, domain and persistence, and manage the persistence directly with SQL It avoids a lot of caos in the read/update/save phase.
is it simple? Then, first, should I use the DDD approach? If really yes, I would let the JPA annotations to split inside the domain. Yes, it's not pure DDD, but we live in the real world and the time to do something simple in the pure way should not be some orders of magnitude bigger that the the time I need to to it with some compromises. And, on the other side, I can write all this stuff in an XML in the infrastructure layer, avoiding to clutter the domain with it. As it's done in the spring DDD sample here.
When you want to update an existing object, you first have to load it through entityManager.find() and apply the changes on that object or use entityManager.merge since you are working with detached entities.
Anyway, modelling rich domain models based on JPA is the perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
The interesting point here is that entity views can also be updatable and support automatic translation back to the entity/DB model.
A mapping for your model could look as simple as the following
#EntityView(EmployeeDO.class)
#UpdatableEntityView
interface Employee {
#IdMapping("persistId")
Long getId();
Long getEmployeeId();
String getName();
void setName(String name);
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
Employee dto = entityViewManager.find(entityManager, Employee.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features and it can also be saved back. Here a sample repository
#Repository
interface EmployeeRepository {
Employee findOne(Long id);
void save(Employee e);
}
It will only fetch the mappings that you tell it to fetch and also only update the state that you make updatable through setters.
With the Jackson integration you can deserialize your payload onto a loaded entity view or you can avoid loading alltogether and use the Spring MVC integration to capture just the state that was transferred and flush that. This could look like the following:
#RequestMapping(path = "/employee/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> updateEmp(#EntityViewId("id") #RequestBody Employee emp) {
employeeRepository.save(emp);
return ResponseEntity.ok(emp.getId().toString());
}
Here you can see an example project: https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-webmvc

Hibernate associations with an interface

I'm hoping someone can help me figure out a problem I am having with hibernate.
I am working on a billing system where different types of entities can be invoiced. The entities implement an "Invoicable" interface.
In my invoice class I have this line
#ManyToOne(fetch = FetchType.EAGER)
private Invoiceable responsibleEntity;
The invoiceable interface looks like this
#MappedSuperclass
public interface Invoiceable
{
// Name to display as entity responsible
String getInvoiceOwnerName();
// Get email address to send invoice
String getInvoiceOwnerEmail();
}
I'm getting an exception when I run this
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on models.Invoice.responsibleEntity references an unknown entity: interfaces.Invoiceable
I've tried googling it and reading some similar posts but I can't get anything to work. Does hibernate support this type of mapping?
Thanks
You have to use Inheritance in the Hibernate way to do this. Details can be found in the --> Hibernate Inheritance Doc
Additional information:
Hibernate is not able to wire types together where no database table is behind it. What does this mean for you. MappedSuperclasses do not have a table so there's nothing to wire together. So this is out of business. Most important keep in mind. When you request to read all parent objects. To which tables should he join? This is the reason why the inheritance system of hibernate comes in to play. You have to change the interface to a normal class. This should not be to much of a problem. Now choose an inheritance Strategy of your choice. Each of them have their pros and cons. Please read the doc to get an idea of which is best for your situation.
The following is an example of a the single table approach with an Invoice class.
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="invoiceType",
discriminatorType=DiscriminatorType.STRING
)
#DiscriminatorValue("Invoice")
public class Invoiceable { ... }
#Entity
#DiscriminatorValue("CustomInvoice")
public class CustomInvoice extends Invoiceable { ... }

What is appropriate way of creating objects with One-to-Many relationship using Objectify and RequestFactory?

What is appropriate way of creating objects with One-to-Many relationship using Objectify and RequestFactory? I've read documentation for these libraries, and also reviewed number of sample projects such as listwidget and gwtgae2011. All of them use #Embedded annotation which is not what I want because it stores everything within one entity. Another option according to documentation would be to use #Parent property in child classes. In my example (getters/setters removed for simplicity) I have entities Person and Organization which defined as
#Entity
public class Person extends DatastoreObject
{
private String name;
private String phoneNumber;
private String email;
#Parent private Key<Organization> organizationKey;
}
and
#Entity
public class Organization extends DatastoreObject
{
private String name;
private List<Person> contactPeople;
private String address;
}
Now if I understood documentation correctly in order to persist Organization with one Person I have to persist Organization first, then set organizationKey to ObjectifyService.factory().getKey(organization) for Person object and then persist it. I already don't like that I have to iterate through every child object manually but using RequestFactory makes everything is more convoluted due to presence of proxy classes. How would I define Organization and OrganizationProxy classes - with Key<> or without it ? Will I have to define something like this in Organization ?
public void setContactPeople(List<Person> contactPeople)
{
for (int i = 0; i < contactPeople.size(); ++i)
{
DAOBase dao = new DAOBase();
Key<Organization> key = dao.ofy().put(this);
contactPeople.get(i).setOrganizationKey(key);
}
this.contactPeople = contactPeople;
}
And how would I load Organization with its children from Datastore ? Will I have to manually fetch every Person and fill out Organization.contactPeople in #PostLoad method ?
It seems like I'll have to write A LOT of maintenance code just to do what JPA/JDO does behind the scene. I simply don't get it :(
Am I missing something or it's the only way to implement it ?
Thanks a lot for answers in advance!!!
You need to make it as #Parent only when you going to use it in transaction against all Person in this Organization. I'm sure it's not what you want.
It's enough to save just private Key<Organization> organizationKey, and filter by this field when you need to find Person for specified Organization
As about loading all referenced objects - yes, it is, you have to load it manually. It's pita, but it's not a lot of code.
Also, there is a different way to store this relationship, if your organization are small enough, and consists of few hundreds of people. At this case you can have List<Key<Person>> contactPeopleKey;, and load all this people by existing Key, manually, it much be much faster than loading by new Query

Hibernate Annotation for Entity existing in more than 1 catalog

I have a Person entity mapped by Hibernate to a database table in a database catalog "Active". After a period of time, records in this database table in the "Active" catalog are archived/moved to an exact copy of the table in a database Catalog "History". I have the need to retrieve from both the Active and History Catalogs. Is there a better way to model this with Hibernate annotations than making an abstract class that 2 classes extend from.
This is what I have now.
#MappedSuperclass
public abstract class Person {
#Id
private Integer id;
private String name;
}
#Entity
#Table(name="Person", catalog="Active")
public class PersonActive extends Person {
}
#Entity
#Table(name="Person", catalog="History")
public class PersonHistory extends Person {
}
To my knowledge, that would be the right way to do it with annotations (you kinda have two tables so you need two entities). Then run a polymorphic query on the Person entity. I find this pretty clean by the way.
PS: Can you add a pointer on how to do this with mapping files, I'm really curious.
My thought would be to write a query to select both tables from db A and B. then create a query with hibernate and map it to your class.
example:
#Entity
#NamedNativeQuery(
name="GetAllPerson",
query="select * from A.Person inner join B.Person on A.Person.Id = B.Person.Id"
)
public class Person {
...
}
Not sure if it could work, your question made me also curious about the best way to do it :). I'll test it tonight after work and see if its any good.
I think there is a subproject of hibernate named shards. It is designed to work with multiple relational databases. If you want to use it, you may need big changes in your code.

Categories