How to Autowire business objects in Spring - java

I'm following the Controller -> Service -> DAO pattern. When I call a DAO implementation, I get back a DTO/Data object. Which then gets passed to the service layer, bringing together it's respective business object and it's data object. Like so:
public User getUserById(int id) {
return new User(userDAO.getUserById(id));
}
class User {
private UserDTO userDTO;
public User(UserDTO userDTO) {
this.userDTO = userDTO;
}
}
What I'd like to do is wrap ALL my business logic inside the business class but I require additional dependencies.
For example, I'd like to be able to do something like this:
//... some code
User user = userService.getByUserId(1);
user.delete(); // this should delete the user from the database
In order for me to delete the user this way, I would need to Autowire the UserService into the business class but this will not work since I am manually instantiating the User class in the User Service.
class User {
#Autowired
private UserService userService; // this won't work since I call instantiate User myself, ie. new User()
private UserDTO userDTO;
public User(UserDTO userDTO) {
this.userDTO = userDTO;
}
public boolean delete() {
userService.deleteByUserId(userDTO.getId());
}
}
Is there a pattern I can follow to allow me to do what I want?

I don't think it's a good design to have business class as a member of your DTO objects.
A more proper approach would be to have your delete method in the business class. That would help loose coupling.

I think you can do this with the #Configurable annotation, though this really isn't how Spring is supposed to work. It will add lots of overhead to your application and make debugging harder.
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable

Related

(Spring Boot, JpaRepository, Postgresql) Why is repository interface is not instanced during runtime?

I'm making a small program using Spring, Maven and Hibernate. The current goal is to use JpaRepository to interact with a Postgresql database. However, when I try to call for it to list all entries in a table within the database, it spits out a NullPointerException. Online resources vary in their implementation, so it's been hard for me to understand what goes wrong.
My application can be summarized as follows:
Javatest3Application.java (Outermost layer, handles communication)
#SpringBootApplication
#EnableAutoConfiguration
#EnableJpaRepositories
#RestController
public class Javatest3Application {
//---VARIABLES---
private JavatestService service_handler = new JavatestService();
//---PUBLIC---
public static void main(String[] args) {
SpringApplication.run(Javatest3Application.class, args);
}
#PostMapping("/login")
public ResponseEntity<String> Login(#RequestBody Map<String, String> json_map) {
//>>Read json_map for account_name and account_pwd
//Ask Service layer to log user in
Long session_id = this.service_handler.OpenSession(account_name, account_pwd);
//>>Construct response, blah blah...
}
}
JavatestService.java (Middle layer, manages repository interaction)
#Service
public class JavatestService {
//---VARIABLES---
#Autowired
private JavatestRepository repo;
//---PUBLIC---
public JavatestService() {}
public Long OpenSession(String in_name, String in_pwd) {
//Call database for credentials
List<JavatestUser> user_listings = this.repo.findAll(); //<- THIS FAILS
//>>Go though list, blah blah...
}
}
JavatestRepository.java (Bottom layer, interface extention)
#Repository
public interface JavatestRepository extends JpaRepository<JavatestUser, Long> {
//List<JavatestUser> findAll(); <- Don't think I need to add this. I believe its already in JpaRepository
}
JavatestUser.java (Bottommost layer, DTO class for database entry)
#Entity
#Table(name = "javatest_table", schema = "javatest_schema")
public class JavatestUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long account_id;
private String account_name;
private String account_pwd;
public JavatestUser(){
}
//>>Getter and Setter functions, blah blah...
}
So, as far as I have understood it, we cannot instance objects of an interface. However, when using Spring, the program itself creates classes that implement the interface, and then hands such a derived class back to us via the #Autowired keyword.
Thus when we call the findAll() function, we use that derived class to fetch objects of the associated #Entity class.
From my research I've come to believe I might use the #Service keyword wrong, and that it perhaps should be a #Controller. However, as far as I can see, the are implementations of both alternatives, so my understanding of what differentiates them is somewhat lacking. Regardless of which I am using, the #Autowired doesn't seem to provide any JavatestRepository-derived object for me to call findAll() upon.
EDITS
Added #EnableJpaRepositories in accordance with Eugene Botyanovsky's suggestion.
You are probably missing annotation, which enables all your repositories:
#EnableJpaRepositories
It's similar to #EnableAutoConfiguration you used, but exactly for JPA repositories.

Spring mvc - which layer should convert entities to dtos (and vice versa)

In which layer should DTO/Entity conversion take place.
Having following structure in a Spring Mvc application:
Controller
Service
Repository
The approach I'm using now, where service layer is #Transactional.
#RestController
public class ExampleController {
#Autowired
private ExampleService exampleService;
#Autowired
private ExampleMapper exampleMapper;
#GetMapping("/examples")
public ResponseEntity<List<ExamleDto>> getAll() {
var examples = exampleService.getAll();
return ResponseEntity.ok(exampleMapper.examplesToExampleDtos(examples));
}
#PostMapping("/examples")
public ResponseEntity<Void> create(#RequestBody #Valid ExampleCreateDto createDto) {
var example = exampleService.create(createDto)
return ResponseEntity.created(URI.create("examples/" + example.getId()).build();
}
// PUT, DELETE, ...
}
#Service
#Transactional
public class ExampleService {
#Autowired
private ExampleRepository exampleRepository;
#Autowired
private ExampleMapper exampleMapper;
public List<Examle> getAll() {
var examples = exampleRepository.findAll();
return examples;
}
public void create(ExampleDto exampleDto) {
var example = exampleMapper.asExample(exampleDto);
return exampleRepository.save(example);
}
}
public interface ExampleRepository extends JpaRepository<Example, Long> {
Why I choose this aproach:
The service layer is transactional, so whenever we get back to the controller, all changes will be flushed (version field for example) will all be set.
It makes you think about your entitygraph, lets say you have a Person entity which has a list of Deparments. Lets say the PersonDto contains also the list of DeparmentDtos, it forces you to fetch all deparments before hand or you will run into a LazyInitializationException in the controller layer.
Which in my opinion is a good thing, because if you would perform the mapping in the service you would be doing N + 1 queries (N being the number of deparments) without realizing it.
Services who need each other to perform there business tasks, work on the entity model instead of the DTO model, which might have some validation (#NotNull, #Size, ...) which only supposed to be valided when it comes from the outside, but internally not all validations should be applied.
Business rules will still be checked in the service layer as part of the service method.
The only thing here is that for update/creates service still communicate by passing dtos iso of entities.
I googled this topic a lot, but couldn't find a definitive answer.

Bind Principal to MyCustomUser class as method argument in controller

I know you can get a username easily in a Spring controller by including Principal as a method argument like:
#GetMapping("/username")
#ResponseBody
public String currentUserName(Principal principal) {
return principal.getName();
}
But I am ultimately going to want access to members of a MyCustomUser class that I instantiate from a repository with a findBy method. I can put a helper method in the Controller to do the lookup and return the user based on principal.getName(), but can I go a step further and bind to MyCustomUser directly, like
#GetMapping("/stuff")
#ResponseBody
public String stuff(MyCustomUser user) {
return user.thing();
}
I was looking into creating a converter like (Ref):
#Component
public class PrincipalToMyCustomUserConverter implements Converter<Principal, MyCustomUser> {
private MyCustomUserRepository userRepository;
public PrincipalToApplicationUserConverter(MyCustomUserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public MyCustomUser convert(Principal source) {
return this.userRepository.findByUsername(source.getName());
}
}
But I don't know if that's an appropriate way to grab the repository, and I don't know how to pass the repository when registering the converter (Ref).
You're correct in that the converter you're proposing is not appropriate. Your converter can convert from an object of type Principal to an object of type MyCustomUser, however, there is no *Principal* by which to convert. The magic behind the principal injection is that Spring actually gets this from the SecurityContextHolder, it is not deserialized from request...though fields present in the request allow Spring to create the Principal. If you truly want to inject MyCustomUser, use a ModelAttribute. ModelAttributes are available to all of your Spring controller methods.
I generally like to keep stuff like this in it's own class, so I would define a class that held this and other #ControllerAdvice in one place, something like this:
#ControllerAdvice
public class SomeControllerAdvice {
#Autowired
private MyCustomUserRepository myCustomUserRepository;
#ModelAttribute
public MyCustomUser getUser(Principal principal) {
return myCustomUserRepository.findByUsername(principal.getName());
}
}
The above should suffice to make MyCustomUser available to all methods. I would note that you probably want a little error handling here, like skip over if principal is null and whatnot, also have your findByUsername method return an Optional so your can address empty returns.
see:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

Can Spring DAO be merged into Service layer?

I'm developing a web application with spring framework and mybatis.
In most cases(at least for me), DAO's methods are very short something like this:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
public User getUser(String userId) {
return (User) getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
}
}
So basically, I need to write a method(e.g. getUser(String userId)) in DAO for each query which is being forwarded to service objects where it is being used. It seems unnecessarally redundunt to me.
My co-worker is trying to make it simple. He wrote CommonDao like this:
#Repository
public class CommonDao {
#Autowired
private SqlSessionTemplate sqlSession;
public Object insert(String queryId, Object params) {
return sqlSession.insert(queryId, params);
}
public Object update(String queryId, Object params) {
return sqlSession.update(queryId, params);
}
public Object delete(String queryId, Object params) {
return sqlSession.delete(queryId, params);
}
public Object selectOne(String queryId) {
return sqlSession.selectOne(queryId);
}
public Object selectOne(String queryId, Object params) {
return sqlSession.selectOne(queryId, params);
}
}
So we can use these methods in services like:
#Service
public class CrudService {
...
#Autowired
private CommonDao commonDao;
...
public UserDto selectUser(Integer userId) {
...
UserDto userDto = (UserDto) commonDao.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
...
}
}
I'm kinda like this approch since it makes codes simpler. But I'm not sure it is a good prectice to follow.
To avoid a boilerplate code and at the same time have type safety and leave your service layer free from DAO implementation details use spring-mybatis MapperScannerConfigurer.
In this case you can replace your DAOs with type-safe mappers.
The equivalent of your DAO
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
public User getUser(String userId) {
return (User)getSqlSession().selectOne(
"org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
}
}
will be this mapper class
package org.mybatis.spring.sample.mapper;
interface UserMapper {
User getUser(String userId);
}
If you rename it to UserDao you will not need to change your services at all. Service only depends on the declared mapper interface.
Note that you need to define this interface in order to have type safety and also define the dependency of you service.
Of course you need configure spring-mybatis so that it generates mapper implementation based on the mapper interfaces defined in your code. This is rather straightforward and there are many options how to do that.
Hmm, what you are struggling with, is normal in MyBatis.
Your co-worker pointed you in some direction... But what is the real value of CommonDao in this shape? For me it's not very useful. You still need almost the same amount of code - and still you have to do a lot of casting.
As #Rom Konoval said, there is MapperScannerConfigurer which can generate mapper implementations - this way you don't write redundant implementations and have the benefit of a type safety - type casting still happens but is hidden from you. You can try it.
Here is a sample usage on the GitHub.
Alternatively, you can create DAO implementations by yourself (as you already did) or just use the SqlSessionTemplate directly in your Services. Up to you. Just keep your code base as small as possible and follow a common sense.

Categories

Resources