I'm using MongoDB in my spring boot based application. I persist a set of entities which look like this:
#Document(collection = "users")
public class User {
#Id
private String id;
private String email;
}
It has some required annotations in order to be persisted by spring data.
Now I want to extract all entity classes from my maven module and include them in a new api-module which we'll be shared among other different modules in my project.
I don't want to include mondodb-related annotations into this module, I want it to be simple java classes like this.
public class User {
private String id;
private String email;
}
So when I will use it in my module (with mondogdb) I will have to add the appropriate annotations:
#Document(collection = "users")
public class UserEntity extends User {
}
So the only one way I can see is to extend POJO and enhance it with mongo annotations.
The question is are there any better ways how this can be implemented. Couldn't find any solutions yet.
Related
I have defined some beans to be stored in a Mongo Database. I am using Quarkus Panache MongoDB.
It works if I annotate the domain classes with #MongoEntity
#MongoEntity(collection="ThePerson")
public class Person {
public ObjectId id;
public String name;
...
}
I want to avoid the #MongoEntity in these classes, to put these clases in a separated package and not depend on the Panache, or even any quarkus dependency.
How can I configure these external beans?
Why? I want to share a package to help other teams that don't use quarkus but will use the same model.
What if you share an interface with all the getters for example? So you can easily share your interfaces in packages/libs.
With this approach, suppose you are going to expose your models over some REST apis returning Jsons for your clients, you can create a utility jar/package that automatically converts the Json for your pojos.
//your abstration
interface BasePerson {
ObjectId id();
String name();
...
}
//your model/entity
#MongoEntity(collection="ThePerson")
public class Person implements BasePerson {
public ObjectId id;
public String name;
...
}
I have an entity as
#Getter
#Setter
#Entity
#Table(name = "feature")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Feature {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#OneToMany(mappedBy = "featureId", fetch = FetchType.LAZY)
private transient Collection<FeatureComponent> components;
}
While in its Repository(Dao) file, I have
public interface FeatureDao extends JpaRepository<Feature, Integer> {
#Query("SELECT e FROM Feature e")
public List<Feature> getAll();
#Query("SELECT e FROM Feature e LEFT JOIN e.components fc WHERE e.id= :id")
public Feature getWithDetail(#Param("id") Integer id);
}
When I'm calling featureDao.getAll(); it returns all features but including components list filled and because of that, my response it being too large to load on client-side.
I'm unable to understand why it is happening when I'm using Lazy fetch mode and didn't mentioned joining with components in my getAll method.
Please help to resolve that issue,
Thanks in advance.
Just like #spOOm already mentioned I also suspect this is the side effect of Jackson Feature entity serialization into JSON triggering the load of all the components.
That is why using DTOs instead of plain Entities is usually advisable when returning data via a Controller. With DTOs, you clearly define whatever you want to include in the response to the caller. You can even reorganize your model so that it fits better the needs of the clients. You decouple your inner model and the model your API consumers know, making it possible to rework your inner model and still keep the same public model. You could have the following DTO.
public class FeatureSimpleDto {
private Integer id;
private String name;
private String description;
}
Then, in your Controller or Service (here you can find different opinions) you would basically convert your Feature entity into a FeatureSimpleDto that would be returned by your Controller. Here you can use mapping libraries such as MapStruct or you can do it on your own (I usually tend to prefer doing it on my own, one less dependency to rely on).
Using Lombok may be a problem, depending on the relationship between tables, try to create getters manually in entity classes.
Thanks to everyone for providing workarounds... But every work item requires lots of changes which were not possible for me...
Luckily I found a solution that is working fine for me... Better to post here...
Step-1: Add dependency in pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
Step-2: Add a 'Bean' for Hibernate Module
We can add bean in any file having #Configuration annotation... Even, we can add in Springboot main application file(where we have main method).
#Bean
public Module datatypeHibernateModule() {
return new Hibernate5Module();
}
That's it, Happy Coding...
I'm learning spring 5 MVC with spring data JPA and sql queries
I'm trying to use a native query in my spring api restful example but when I run the project, my method returns an empty json object.
My Model class
#Entity
#Table(name = "bicycle")
public class Bicycle
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="name")
private String name;
#Column(name="year")
private int year;
}
My repository class
#Repository
public interface BicycleRepository extends CrudRepository<Bicycle, Long>
{
#Query(value = "SELECT * from db_bicycles.bicycle", nativeQuery = true)
public List<Bicycle> obtenerTodos();
}
My controller class
#RestController
#RequestMapping("/api")
public class BicycleController
{
#Autowired
private BicycleRepository bicycleRepository;
//create get all bicycles
#GetMapping("/bicycles")
public List<Bicycle> getAllBicycles()
{
return bicycleRepository.obtenerTodos();
}
}
My application properties
spring.datasource.url=jdbc:mysql://localhost/db_bicycles?useUnicode=true&useJDBCCompilantTimeZoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql=true
This table have 2 rows.
use getter/setter in your POJO class
you need not to write query to retrieve all record. Simply you can make use of findAll() method
which is available in crudeRepository.
bicycleRepository.findAll(); (will return list of your POJO class.)
make sure, there is data present in table. if not then make use of Setter/Construction
Injection to populate data. OR you can make a POST call to populate data in table using save() method.
If you are using lombok and Spring Tools Suite in your project, then try running Maven Clean and Maven Install.
mvn clean install
After that, you can run the project. It should work.
The solution is to add the getter and setters, eclipse (STS) only add me the setters
I have Spring Boot Application with Web, JPA, H2, Web, Lombok dependency.
I have entities as follows
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private Integer pageCount;
#ManyToOne
#JoinColumn(name = "author_id")
private Author author;
}
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String firstName;
#Column
private String lastName;
}
I am getting following JSON in repose for the /books REST endpoint
[
{},
{}
]
If I add getters and setters to entity it works fine. How can I get the actual values in response without adding getters setters as I am using #Data annotation
First of all , In spring it is not a good approach to return the entity object directly from a controller to be converted to json. Use the DTO approach so that you will always have a DTO object returned from the controller endpoint. This way you will have more control over the type and structure of data you want to retun from the endpoint.
Read here about the advantages of using a DTO .
Secondly, verify that your tables follow the basic spring jpa naming conventions or is same as the entity class names if not please add the #Table(name="") annotation to specify the table name. Check that data is being populated to your entity classes.
Remove the #Data annotation.
#Data should not be used on JPA Entities as Entity toString, equals, and hashCode methods need to be authored in a very specific manner. See here for details.
After searching a lot and looking on the answers to my question found a solution.
If I run my spring boot app using java -jar from cmd it works fine. But not with IDE so issue was with IDE configuration.
I am using Eclipse IDE
Right Click on your project -> Properties
Then from Properties window
Java Compiler -> Annotation Processing -> Enable Annotation Processing
This option should be checked then annotations gets processed. I followed https://www.baeldung.com/lombok-ide from Tinku's answer on this question.
need to add Lombok plugin in maven or gradle file link How to configure Lombok with maven-compiler-plugin?.
If you are using IDE then need to install Lombok plugin link https://www.baeldung.com/lombok-ide
If you are using Eclipse check to have Lombok installed:
I am going to develop small web application to get an idea of micro-services architecture, using spring-boot and spring-data-jpa.
At initial stage, I have only 2 entities (1) CountryEntity and (2) StateEntity. In monolithic structure which already I have, there is a relationship between these 2 entities like :
CountryEntity:
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
public class CountryEntity extends BaseEntity{
private String countryCode;
private String country;
#OneToMany(mappedBy = "country", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<StateEntity> stateEntities;
}
StateEntity :
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
public class StateEntity extends BaseEntity {
private String stateCode;
private String state;
#ManyToOne
private Country country;
}
Also, please notice that I have extends BaseEntity which contains shared fields like :
BaseEntity :
#MappedSuperclass
#Data
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date createdDateTime;
private Date updatedDateTime;
private Boolean isActive;
private Boolean isDelete;
#PrePersist
private void setCreatedDateTime() {
this.createdDateTime = new Date();
this.isActive = true;
this.isDelete = false;
}
#PreUpdate
private void setUpdatedDateTime() {
this.updatedDateTime = new Date();
}
}
Now, I have 2 queries while converting above monolithic structure to microservices as describe here :
1. Should I create shared library (separate microservice for shared code and then add dependency in all different microservices app) for BaseEntity defined above and other classes like Constants, Utility etc.? or I should repeat all the code in all microservices? Please guide because some sites are suggesting for shared library and some experts are advice not to use shared library and suggesting to repeat all the code. What should I do for better architecture.
2. How can I give relationship between above 2 entities in microservices architecture? I gone through SO questions and some feedback says that it is not possible. Then I have an application, in which all entities are in a relation directly or indirectly. Here, I have just take an example from original application. I know it must be some structure or some way, with which I can apply relation-ship. Please guide me.
Thank you so much in advance.
When you are working in Microservices architecture, you should put all common code into a shared module. This module can be deployed as a library to your Maven or any other repository and injected to your microservices on demand.
If you make a copy of code into your microservices - it will be harder to maintain it further. Imagine 100 microservices, which have a common class inside, and you want to change it. It will be even harder when you decide to split microservices into different repositories.
But you should be very careful when using Spring REST repository. When you inject into your microservice common repository for any entity, it will automatically expose it to REST, so you'll have the same REST points on different microservices, that's not good. There are may be troubles with Spring Security (if you forgot to hide it somewhere).