Spring Mongodb - Insert Nested document? - java

I have the following classes
#Document
public class PersonWrapper {
#Id
private ObjectId _Id;
#DBRef
private Person person
// Getters and setters removed for brevity.
}
public class Person
{
#Id
private ObjectId _Id;
private String name;
// Getters and setters removed for brevity.
}
And - I have the following MongoReposityClass...
public interface PersonWrapperRepository extends MongoRepository<Person, String> {
Person findByPerson_name(String name);
}
Showing the repository class may have been pointless - but basically what I do here is create an instance of the repository class and then I create a PersonWrapper object, then do something like :
repo.insert(personWrapperInstance);
Now, while this will infact work - I find that I need to insert "Person" first, and then add the returned object to the PersonWrapper, then do another insert.
That is fine and all, and I am sure I can write some flow control to catch errors and behave sensibly if something breaks.
HOWEVER - Everyone knows that would be inefficient, because it is two calls to save. There has GOT to be a way I can basically create the nested objects, and do an insert on the ParentWrapper, and have mongo insert the Person instance if it doesn't already exist, right?
I have been googled this, but ran into some issues getting what I wanted to know.

This cannot be done with spring-data-mongodb. The Framework lacks the ability to work with nested objects, and your way of putting it in a try catch is pretty much the only way to do it.

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.

Spring Boot Repository - load DTO's direct from the Database

In my application I use DTOs. My current solution in pseudocode is this - works well:
ResponseEntity<EntityDTO> RestController.get(String uuid){
EntityDTO dto = Service.get(uuid) {
Entity entity = Repository.loadEntity(id);
return EntityDTO.from(entity);
}
return ResponseEntity<EntityDTO>( dto , HttpStatus.OK);
}
Recently I saw an other solution without the transformation step in the service layer.
E.g. your Entity looks like this
:
#Entity
public class Book {
Long id;
String title;
String text;
.....
}
And the text is too 'heavy' to send it with the hole book you usually would create a DTO like this:
public class SlimBookDTO {,
static SlimBookDTO from(Book book) {
return new SlimBookDTO(book.id, book.title);
}
Long id;
String title;
.....
}
The "new" (for me) Solution is to create only an interface like this:
public interface SlimBookDTO {
Long getId();
String getTitle();
}
And your BookRepository gets a new method:
#Repository
public interface BookRepository extends JpaRepository<Book , Long> {
List<SlimBookDTO> findAllByTitle(String title);
}
With this method I don't need the service layer any more for direct requests. Is this common? Does somebody has experience with this? Has it some downsides that I can't see in a small application but will face in larger scale?
Those are couple of ways of returning data from the database.
You create DTO and map necessary fields and return
Other is create an interface which is directly a kind of return type from Repository. this is what we call as JPA interface projection.
For second one, you know in detail by referring below link
https://www.baeldung.com/spring-data-jpa-projections
JPA interface projections are very useful when we query two or more entities in the Repository class
This is totally fine for simple GETs if the objects are straightforward enough, although of course you can't add additional logic, formatting or constraints. But as long as you don't need to do that, this will work well.
I don't think Hibernate analyzes the dto to only select a few fields though, so if you want to improve the performance too you can define the queries yourself, i.e. #Query("select new com.bla.SlimbookDTO(book.id, book.title) from Book book"), at the cost of not being able to just use automagically generated queries anymore based on the method name.

Object-relational mapping(?) with JPA/Eclipselink

I'm working on an exercise where we're supposed to create a car-rental program in Java where all the data should be stored in a PostgreSQL database using JPA and EclipseLink.
I've managed to create a test-class which connects and stores/reads data to/from the database. Now I'm wondering how I should proceed to make this "big" car-rental program work together with the database...
We've got about 10 classes (Car.java, Customer.java, etc.), which I think based on an earlier example, should be connected to the main/client-classes (Customer_Client.java, Admin_Client.java, etc.) using a Controller-class(?). But I'm not quite sure how and why. If I understand it right, I think the database connecting code etc. is supposed to happen in the main/client-classes?
Could someone which is familiar with this kind of programming/modelling (ORM) point me in the right direction about how the Controller-class should work together with the client-classes?
Based on the earlier example, I guess the Controller-class should contain a getCars, getCustomers etc. method for all the classes I need to access in the main/client-classes?
I'm also wondering how I should add "custom"/class attributes (e.g. Adress.java) as an column in a table in the database? When I'm trying using the same method as with the String and Integers for e.g. the Adress attribute, I get this exception:
"Exception Description: The type [class no.hib.dat101.Adress] for the attribute [adress] on the entity class [class no.hib.dat101.Customer] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface."
I guess this has something to do with the database table-column only supports certain datatypes?
A controller class in ORM is usually a DAO. DAO is a pattern which defines how to create/read/update/delete objects from database. A general DAO can look like this:
public interface Dao<E> implements Serializable{
public E find(int id);
public void insert(E entity);
public void update(E entity);
public void delete(int id);
}
And its implementation (for example for Car entity) can look like this:
public class CarDao implements Dao<Car>{
private EntityManager em;
public Car find(int id){
return em.find(id, Car.class);
}
public void insert(Car entity){
em.persist(entity);
}
public void update(Car entity){
em.merge(entity);
}
public void delete(int id){
em.delete(find(id));
}
}
For more info about DAO pattern please see Core J2EE Patterns - DAO (loooong but VERY good reading) or this link (shorter reading, but you will get a general idea about DAO faster :))
Entities update/insert is very easy, for example lets say that you want to set a new address for some customer.
private CustomerDao customerDao;
private Addressdao addressDao;
private int customerId;
public void updateCustomerWithAddress(){
Address address = new Address();
//init address variables
addressDao.insert(address);
Customer customer = customerDao.find(customerId);
//I assume you have a bidirectional oneToOne mapping between address and customer
address.setCustomer(customer);
customer.setAddress(address);
customerDao.update(customer);
}
In case of an exception you are getting, it says that your entities does not implement Serializable interface. So maybe by implementing this interface you will fix your issue, but we can really say much without actually seeing the code itself.
Basing on your exception, you should let no.hib.dat101.Adress implement java.util.Serializable so it is marked to serialize when saving a no.hib.dat101.Customer.
I guess this has something to do with the database table-column only supports certain datatypes?
No, your issue is not related with database. How about adding implementsSerializable to your Adress class declaration?
Read about it more here.

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

What is the best approach to write a data access object (DAO)?

I was trying to write a user authentication system in Java. So I wrote some DAO class. First I did write a class named Persistence which is abstract. It is responsible for holding some common attributes. And wrote a class named User extending Persistence class. Those classes are –
public abstract class Persistance {
private Date createdDate;
private Date lastUpdatedDate;
private long version;
private boolean isDeleted;
//getter and setters
}
and the user class
public class User extends Persistance{
private String username;
private String password;
private String passwordConfired;
// getters and setters
}
My questions are- what is the best way to write variable name, which one is good, createdDate or dateCreated, deleted or isDeleted etc.
And is this approach is okay or is there more good approach ?
And how to implement data versioning?
To write a DAO, typically you create an interface that defines the behavior of the DAO.
interface MyObjDao {
public int save(MyObj myObj);
public void delete (MyObj myObj);
// as many methods as you need for data acess
}
and then you create the actual implementation
class MyObjDaoImpl implements MyObjDao {
// implement methods here
}
The advantages of this are:
1) Because you define an interface, mocking DAOs is easy for any testing framework
2) The behavior is not tied to an implementation -- your DAOImpl could use jdbc, hibernate, whatever
Your Persistance class is really a base class for all entities -- i.e. all classes instances of which get saved, where you want to represent some common fields in one place. This is a good practice -- I wouldn't call the class Persistance, something like BaseEntity is better (IMHO). Be sure to have javadocs that explain the purpose of the class.
With respect to variable names, as long as they make sense and describe what they are for, its good.
so dateCreated or createdDate are both fine; they both get the idea across.
You are mixing a DAO (data access object) and a VO (value object) - also known as a DTO (data transfer object) - in the same class.
Example using an interface for DAO behavior (blammy and kpow might be webservice, oracle database, mysql database, hibernate, or anything meaningful):
public interface UserDTO
{
boolean deleteUser(String userId);
UserVO readUser(String userId);
void updateUser(String userId, UserVO newValues);
}
package blah.blammy;
public class UserDTOImpl implements UserDTO
{
... implement it based on blammy.
}
package blah.kpow;
public class UserDTOImpl implements UserDTO
{
... implement it based on kpow.
}
Example VO:
public class UserVO
{
String firstName;
String lastName;
String middleInitial;
... getters and setters.
}
I prefer to identify the target of the delete using an ID instead of a VO object. Also, it is possible that an update will change the target identified by user ID "smackdown" to have user ID "smackup", so I generally pass an id and a VO.
A good approach would be to use JPA with all of its features, this tutorial was really helpful.
It explains how to use the #PrePersist and #PreUpdate annotations for setting create and update timestamps. Optimistic locking is supported by the #Version annotation.
My questions are- what is the best way to write variable name, which
one is good, createdDate or dateCreated, deleted or isDeleted etc.
createdDate or dateCreated is very subjective. In databases, I have mostly seen createdDate though. Between deleted and isDeleted, I prefer (again subjective) deleted. I think the getter method can be named isDeleted().

Categories