I am writing my first Spring MVC webapp and have a question about DAOs and web service requests.
Essentially my app allows the user to construct an order, which gets persisted to a database via a DAO. Later on a scheduled worker will retrieve the new orders from the database, submit them to a third-party SOAP service and update each order with some details (e.g. order number).
My controller calls the OrderService (a condensed version):
#Service
public class OrderService {
#Autowired
private OrderDao orderDao;
public List<Order> getOrderList() {
List<Order> orders = orderDao.getAllOrders();
return orders;
}
public void addNewOrder(Order order) {
orderDao.addOrder(order);
}
}
The OrderService calls the OrderDao:
#Repository
public class OrderDao extends JdbcDaoSupport {
#Autowired
public OrderDao(DataSource dataSource) {
setDataSource(dataSource);
}
public List<Order> getAllOrders() {
String sqlQuery = "SELECT id, name, status, orderNumber FROM orders";
List<Order> orders = getJdbcTemplate().query(sqlQuery, new OrderRowMapper());
return orders;
}
public int addOrder(Order order) {
String sqlQuery = "INSERT INTO orders (name, status) VALUES (?, ?)";
getJdbcTemplate().update(sqlQuery, new Object[] { order.getName(), order.getStatus() });
return getJdbcTemplate().queryForObject("SELECT LAST_INSERT_ID()", Integer.class );
}
}
The Order model looks like:
public class Order {
private int orderId;
private String name;
private String status;
private String orderNumber;
// getters and setters etc.
}
At present my OrderDao only communicates with the database to perform CRUD actions on the Order model. I am not sure whether I should create a placeOrder() method within the OrderDao as this would mean I have a single DAO that accesses both database and SOAP service, which feels wrong.
It also feels wrong to put placeOrder() in the OrderService because the service will contain a mixture of internal DAO calls and external third-party SOAP calls.
I've been reading up on interfaces but I don't think they help me here as my database DAO would contain create(), update(), delete() which wouldn't apply to a SOAP DAO.
Should I just create two DAOs: OrderDaoDatabase and OrderDaoSoap?
The point of using layered architecture is to encourage decoupling and separation of concerns. You already have the service layer to take care of business logic and data access layer (DAOs) to communicate with the database, that seems to be right. OrderService should talk to the database and OrderDAO should talk to the database.
Your scheduled worker seems to be a different class. OrderDAO can expose the order(s) data through different methods (which are required by your application). If placeOrder() is a call to external web service, it's okay to call that from an appropriate method within OrderService or a different class if required. Now, since that call isn't done at the time addOrder() is called, it probably belongs to a different method which is invoked by the scheduler. On the other hand, I don't think placeOrder() should go into OrderDAO, it should be left for what it says - data access object.
Related
In my microservice written on spring-boot I have following DTO and Entity:
#Entity
class Order {
#Id
private Long id;
#OneToMany
private List<Product> products;
// getters setters
public Product getMainProduct() {
final String someBusinessValue = "A";
return products.stream()
.filter(product -> someBusinessValue.equals(product.getSomeBusinessValue()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No product found with someBusinessValue 'A'"));
}
}
class OrderRequest {
private List<ProductDto> productDtos;
// getters setters
public ProductDto getMainProductDto() {
final String someBusinessValue = "A";
return productDtos.stream()
.filter(productDto -> someBusinessValue.equals(productDto.getSomeBusinessValue()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No productDto found with someBusinessValue 'A'"));
}
}
As seen both entity and dto contain some business logic method for taking the "main" product from the list of all product. It is needed to work with this "main" product in many parts of the code (only in service layer). I have received a comment after adding these methods:
Design-wise you made it (in Dto) and the one in DB entity tightly coupled through all layers. This method is used in services only. That means that general architecture rules for layers must apply. In this particular case, the "separation-of-concerns" rule. Essentially it means that the service layer does not need to know about the request parameter of the controller, as well as the controller shouldn't be coupled with what's returned from service layer. Otherwise a tight coupling occurs. Here's a schematical example of how it should be:
class RestController {
#Autowired
private ItemService itemService;
public CreateItemResponse createItem(CreateItemRequest request) {
CreateItemDTO createItemDTO = toCreateItemDTO(request);
ItemDTO itemDTO = itemService.createItem(createItemDTO);
return toResponse(itemDTO);
}
In fact, it is proposed to create another intermediate DTO (OrderDto), which additionally contains the main product field:
class OrderDto {
private List<ProductDto> productDtos;
private ProductDto mainProductDto;
// getters setters
}
into which the OrderRequest will be converted in the controller before passing it to the service layer, and the OrderEntity already in the service layer itself before working with it. And the method for obtaining the main product should be placed in mapper. Schematically looks like this:
---OrderRequest---> Controller(convert to OrderDto)
---OrderDto--> Service(Convert OrderEntity to OrderDto) <---OrderEntity--- JpaRepository
Actually, this leads to a lot of additional conversions within the service, some unusual work, for example, when updating an entity, now you have two intermediate dto (one from the controller for the update), the other from the repository (the entity found for the update and converted to intermediate dto in service layer) and you need to adopt the state of one into the other, and then map the result to the entity and do update.
In addition, it takes a lot of time to refactor the entire microservice.
The question is, is it a bad approach to have such methods in the entity and incoming dto, though these methods are used only in service layer, and is there another approach to refactor this?
If you need any clarification please let me now, thank you in advance!
I'm new to Spring Boot and I have a question. I will present it as this.
DAO is a design pattern and It has three components. Those are
DAO interface - use to declare methods that need to perform on DTO
DAO interface implementation -use to take data from data source(database)
DTO(Data Transfer Object) - use to retain data to transport among layers.
For example, If we get Student.
Student.java (DTO)
public class Student {
private String name;
private int rollNo;
Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
//getters and setters
}
StudentDao.java
import java.util.List;
public interface StudentDao {
public List<Student> getAllStudents();
public Student getStudent(int rollNo);
public void updateStudent(Student student);
public void deleteStudent(Student student);
}
StudentDaoImpl.java
import java.util.ArrayList;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
//list is working as a database
List<Student> students;
public StudentDaoImpl(){
students = new ArrayList<Student>();
Student student1 = new Student("Robert",0);
Student student2 = new Student("John",1);
students.add(student1);
students.add(student2);
}
#Override
public void deleteStudent(Student student) {
students.remove(student.getRollNo());
System.out.println("Student: Roll No " + student.getRollNo() + ", deleted from database");
}
//retrive list of students from the database
#Override
public List<Student> getAllStudents() {
return students;
}
#Override
public Student getStudent(int rollNo) {
return students.get(rollNo);
}
#Override
public void updateStudent(Student student) {
students.get(student.getRollNo()).setName(student.getName());
System.out.println("Student: Roll No " + student.getRollNo() + ", updated in the database");
}
}
It is about the DAO. In spring boot, there is JPARepository interface. This interface has been declare the methods such as save(), findById(). and we can use the JPARepository as follows.
#Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
Then after we can use this interface the Controller class.
SoftwareEngineerController.java
#RestController
#RequestMapping(value = "student")
public class SoftwareEngineerController {
#Autowired
StudentRepository studentRepository;
//to retrieve all software engineers
#GetMapping("/students")
public List<Studentr> getAllEngineers(){
return studentRepository.findAll();
}
I feel that the JPARepository interface and the DAO interface are doing the same task. (declare the essential methods are needed to perform to dealing with the data)Spring boot is containing the JPARepository hence no more need the DAO is inside the spring boot applications. Is it true?. I'm new to spring boot so if you can give me a good explanation I will highly appreciate it.
They are basically two design patterns. DAO Pattern and Repository Pattern.
So you don't need to use use them both. If you are using Repository pattern, much of heavy lifting is done by spring data jpa itself. Also lot of boilerplate code can be removed.
In your example you can use repository pattern. Also In my opinion, it is better to use a service. Instead of directly calling repository from a controller, you can call the service (SoftwareEngineerService) and from service you can call a repository. So business logic will remain in service layer.
Some more details about Repository vs DAO
Repository:
It's a repository of a specific type of objects - it allows you to search for a specific type of objects as well as store them. Usually it will ONLY handle one type of objects. here in your example Student. You should not deal with other entities there.
A repository will most likely store all data in the same table, whereas the pattern doesn't require that. The fact that it only handles one type of data though, makes it logically connected to one main table (if used for DB persistence).
DAO - data access object (in other words - object used to access data)
A DAO is a class that locates data for you (it is mostly a finder, but it's commonly used to also store the data). The pattern doesn't restrict you to store data of the same type, thus you can easily have a DAO that locates/stores related objects. You can have a DAO class to return diffrent type of objects wich are related but not same.
Finally: Note that both patterns really mean the same (they store data and they abstract the access to it and they are both expressed closer to the domain model and hardly contain any DB reference), but the way they are used can be slightly different, DAO being a bit more flexible/generic, while Repository is a bit more specific and restrictive to a type only.
While working with a project that involves requesting multiple data types from a database I came to a following question:
Lets say I have 2 java classes that correspond to database entities:
Routes
public class Route {
public Route(int n, int region, Date fdate, boolean changed, int points,
int length) {
super();
this.n = n;
this.region = region;
this.fdate = fdate;
this.changed = changed;
this.points = points;
this.length = length;
}
}
Carrier
public class Carrier {
public Carrier(...) {
this.id = src.getId();
this.name = src.getName();
this.instId = src.getInstId();
this.depotId = src.getDepotId();
}
If so, what's the correct approach of creating Dao interfaces and classes? I'm doing it like this -
#Repository
public class CarrierDaoImpl implements CarrierDao{
#Autowired
DataSource dataSource;
public List<Carrier> getAllOrgs() { ... }
}
#Repository
public class RoutesDaoImpl implements RoutesDao {
#Autowired
DataSource dataSource;
public ArrayList<AtmRouteItem> getRoutes(AtmRouteFilter filter) { ... }
}
I'm creating a #Repository DAO for every java class item\db entity and then 2 separate controllers for requests about carriers and routes. Like this:
#RestController
#RequestMapping(path = "/routes")
public class RoutesController {
#Autowired
RoutesDao routesDao;
#GetMapping(value = {"/getRoutes/", "/getRoutes"})
public ArrayList<Route> getRoutes() { ... } }
And same for controller Carriers. Is it correct and if not what's the correct approach?
Sorry for styling issues, that's my first question on stackoverflow :)
I would suggest creating services marked with #Service annotation (i.e. CarrierService interface and CarrierServiceImpl implementation). Than inject them into controllers. Use repositories within services because some database operations will require transactions and a better place for managing transactions are services. Also services can do more specialized job which will require access to multiple repositories so you can inject them. And don’t forget to mark your services with #Transactional annotation.
It's correct to have a DAO for each entity.
When working with JPA repositories you have no choice but to provide the entity. For instance:
public interface FooRepository extends JpaRepository<Foo,Long>{}
Same for the REST controllers, you have to bring together functionalities by object as you do.
You can improve your mapping to be more RESTful. To retrieve all routes, don't specify a path:
#GetMapping
public ArrayList<RouteResource> getRoutes() { ... }
(I never use #GetMapping yet but it should work like that)
And if you want specific route:
#GetMapping("/get/{id}")
public RouteResource getRoute() {...}
You should return resources instead of entities to client.
I'm currently messing around with a Spring Boot REST API project for instructional purposes. I have a rather large table with 22 columns loaded into a MySQL database and am trying to give the user the ability to filter the results by multiple columns (let's say 6 for the purposes of this example).
I am currently extending a Repository and have initialized methods such as findByParam1 and findByParam2 and findByParam1OrderByParam2Desc and etc. and have verified that they are working as intended. My question to you guys is the best way to approach allowing the user the ability to leverage all 6 optional RequestParams without writing a ridiculous amount of conditionals/repository method variants. For example, I want to give the user the ability to hit url home/get-data/ to get all results, home/get-data?param1=xx to filter based on param1, and potentially, home/get-data?param1=xx¶m2=yy...¶m6=zz to filter on all the optional parameters.
For reference, here is what the relevant chunk of my controller looks like (roughly).
#RequestMapping(value = "/get-data", method = RequestMethod.GET)
public List<SomeEntity> getData(#RequestParam Map<String, String> params) {
String p1 = params.get("param1");
if(p1 != null) {
return this.someRepository.findByParam1(p1);
}
return this.someRepository.findAll();
}
My issue so far is that the way I am proceeding about this means that I will basically need n! amount of methods in my repository to support this functionality with n equalling the amount of fields/columns I want to filter on. Is there a better way to approach handling this, perhaps where I am filtering the repository 'in-place' so I can simply filter 'in-place' as I check the Map to see what filters the user did indeed populate?
EDIT: So I'm currently implementing a 'hacky' solution that might be related to J. West's comment below. I assume that the user will be specifying all n parameters in the request URL and if they do not (for example, they specify p1-p4 but not p5 and p6) I generate SQL that just matches the statement to LIKE '%' for the non-included params. It would look something like...
#Query("select u from User u where u.p1 = :p1 and u.p2 = :p2 ... and u.p6 = :p6")
List<User> findWithComplicatedQueryAndSuch;
and in the Controller, I would detect if p5 and p6 were null in the Map and if so, simply change them to the String '%'. I'm sure there is a more precise and intuitive way to do this, although I haven't been able to find anything of the sort yet.
You can do this easily with a JpaSpecificationExecutor and a custom Specification: https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
I would replace the HashMap with a DTO containing all optional get params, then build the specifications based on that DTO, obviously you can also keep the HashMap and build the specification based on it.
Basically:
public class VehicleFilter implements Specification<Vehicle>
{
private String art;
private String userId;
private String vehicle;
private String identifier;
#Override
public Predicate toPredicate(Root<Vehicle> root, CriteriaQuery<?> query, CriteriaBuilder cb)
{
ArrayList<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(art))
{
predicates.add(cb.equal(root.get("art"), art));
}
if (StringUtils.isNotBlank(userId))
{
predicates.add(cb.equal(root.get("userId"), userId));
}
if (StringUtils.isNotBlank(vehicle))
{
predicates.add(cb.equal(root.get("vehicle"), vehicle));
}
if (StringUtils.isNotBlank(identifier))
{
predicates.add(cb.equal(root.get("identifier"), fab));
}
return predicates.size() <= 0 ? null : cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
// getter & setter
}
And the controller:
#RequestMapping(value = "/{ticket}/count", method = RequestMethod.GET)
public long getItemsCount(
#PathVariable String ticket,
VehicleFilter filter,
HttpServletRequest request
) throws Exception
{
return vehicleService.getCount(filter);
}
Service:
#Override
public long getCount(VehicleFilter filter)
{
return vehicleRepository.count(filter);
}
Repository:
#Repository
public interface VehicleRepository extends JpaRepository<Vehicle, Integer>, JpaSpecificationExecutor<Vehicle>
{
}
Just a quick example adapted from company code, you get the idea!
Another solution with less coding would be to use QueryDsl integration with Spring MVC.
By using this approach all your request parameters will be automatically resolved to one of your domain properties and appended to your query.
For reference check the documentation https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support and the example project https://github.com/spring-projects/spring-data-examples/tree/master/web/querydsl
You can do it even more easily using Query By Example (QBE) technique if your repository class implements JpaRepository interface as that interface implements QueryByExampleExecutor interface which provides findAll method that takes object of Example<T> as an argument.
Using this approach is really applicable for your scenario as your entity has a lot of fields and you want user to be able to get those which are matching filter represented as subset of entity's fields with their corresponding values that have to be matched.
Let's say the entity is User (like in your example) and you want to create endpoint for fetching users whose attribute values are equal to the ones which are specified. That could be accomplished with the following code:
Entity class:
#Entity
public class User implements Serializable {
private Long id;
private String firstName;
private String lastName;
private Integer age;
private String city;
private String state;
private String zipCode;
}
Controller class:
#Controller
public class UserController {
private UserRepository repository;
private UserController(UserRepository repository) {
this.repository = repository;
}
#GetMapping
public List<User> getMatchingUsers(#RequestBody User userFilter) {
return repository.findAll(Example.of(userFilter));
}
}
Repository class:
#Repository
public class UserRepository implements JpaRepository<User, Integer> {
}
I have implemented a CodeService which will retrieve a list of countries from the code table.
In my ShippingService, I would like to check if the order is shipped to a certain country.
In this case, should I be using CodeService or CodeDAO to retrieve the list of countries.
public interface CodeService {
public List<String> getCountryList();
}
#Service
public class CodeServiceImpl implements CodeService {
#Autowired
CodeDAO codeDao
public List<String> getCountryList() {
return codeDao.getCountryList();
}
}
#Service
public class ShippingServiceImpl implements ShippingService {
#Autowired
CodeDAO codeDao;
#Autowired
CodeSevice codeService;
public void addOrder(Order order) {
List<String> countries = codeService.getCountryList();
//List<String> countries = codeDao.getCountryList();
}
}
If you dont have any additional logic such as in this case, I think its best to call directly to the DAO.
Pros: Calling always the service, would allow you to seperate completly the DAO layer from you app code.
Cons: you will create 2 redundant classes for each dao, that will only delegate the call to the DAO.
The services layer should be used for business logic over the fetched data. For example: if you want to add permissions to the fetched countries.
If additional logic would be added to the countries in the future, its best to do a refactoring and create the service.