I just started using Spring Boot and love the simplicity of writing applications with it. I would like to know if and how it can be used to write (non web based) services with it.
I have a repository interface:
public interface OrderRepository extends MongoRepository<Order, String>
{
}
If I use #Autowired in a Spring boot application I can use it to do CRUD operations on my database.
However I would now like to use it in a simple application/test without all the webserver backend stuff.
However if I define the following test the repository interface has nothing to get autowired to:
public class TestMongoInterface
{
#Autowired
private OrderRepository orderRepository;
#Test
public void canReadOrders()
{
List<Order> orders = orderRepository.findAll();
for(Order o : orders)
System.out.println("found: "+o.toString());
}
}
I have found some workarounds which get close, but I if at all possible I would like to share my Repository interfaces between different services which these workarounds prevent.
So my question is: Is there a way to autowire the MongoRepository interface to my application where all the db query code gets generated but not all the other stuff I don't need like the webserver/servlet code?
Try this its working fine when i use JpaRepository. Without autowiring its not possible.
#Configuration
public class TestMongoInterface
{
#Autowired
private OrderRepository orderRepository;
#Autowired
public void canReadOrders()
{
List<Order> orders = orderRepository.findAll();
for(Order o : orders)
System.out.println("found: "+o.toString());
}
}
Related
In my Spring Boot app, I am thinking of using an approach as the following interface and service implementations:
PDFService:
public interface PDFService {
String createPdf(UUID uuid);
}
BrandPDFService:
#Service
#RequiredArgsConstructor
public class BrandPDFService implements PDFService {
private final BrandService brandService;
#Override
public String createPdf(UUID uuid) {
Brand brand = brandService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(brand);
}
}
ProductPDFService:
#Service
#RequiredArgsConstructor
public class ProductPDFService implements PDFService {
private final ProductService productService;
#Override
public String createPdf(UUID uuid) {
Product product = productService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(product);
}
}
For using these services:
// brand way
PDFService pdfService = new BrandService();
pdfService.createPdf(uuid);
// product way
PDFService pdfService = new ProductService();
pdfService.createPdf(uuid);
So, I think I need to use generic and pass it to PDFService and then their implementations, but I am not sure how to make it properly (using generic or passing via constructor). So, in order to use createPdf efficiently without repeating code (I know I can also use Template Pattern method, but I just wanted to know polymorphism side) how should I apply polymorphism to these Spring Boot Services properly?
Since BrandPDFService and ProductPDFService are Spring beans (because you annotated them with the #Service annotation), you should not be instantiating them yourself by using new. Instead, you should let Spring autowire them into the class where you are using them.
Because they are both implementations of interface PDFService, when you autowire them, you need to have something to let Spring distinguish them. Otherwise, if the field you are autowiring them in is of type PDFService, Spring won't know which implementation of the interface to autowire. You can give the beans names and use the #Qualifier annotation:
#Service("brandPDFService")
public class BrandPDFService implements PDFService { ... }
#Service("productPDFService")
public class ProductPDFService implements PDFService { ... }
// Example controller where you autowire them
#RestController
public class MyController {
#Autowired
#Qualifier("brandPDFService")
private PDFService brandPDFService;
#Autowired
#Qualifier("productPDFService")
private PDFService productPDFService;
// ...
}
So, I think I need to use generic and pass it to PDFService and then their implementations
I don't know why you think you need to use generics; this doesn't have anything to do with generics.
I've already read these questions and none of them worked:
Spring boot MVC - Unable to Autowire Repository in the service class
Why can't #Autowired a JPA repository - Spring boot + JPA
JpaRepository getting Null at service class
And also this one: https://www.baeldung.com/spring-autowired-field-null
Unfortunately, none of them worked.
What I have is:
Service interface:
#Service
public interface DayTradeService {
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens);
}
Service Implementation:
public class DayTradeServiceImpl implements DayTradeService {
#Autowired
private DayTradeRepository dayTradeRepository;
#Override
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens) {
// Several lines of code and some of them is trying to use dayTradeRepository.
}
}
My DayTradeRepository:
#Repository
public interface DayTradeRepository extends JpaRepository<DayTrade, Integer> {}
Inside my DayTradeController (annotated with #Controller), I can use a dayTradeRepository with #Autowired. But inside a service class, I cannot use. I get this message:
Cannot invoke "meca.irpf.Repositories.DayTradeRepository.getDayTrades()" because "this.dayTradeRepository" is null"
How can I make it possible?
EDIT after I accepted Nikita's answer:
I didn't post the Controller code, but it didn't have the #Autowired for the service class DayTradeServiceImpl. That was the point I was missing. After Nikita pointing that, I could solve the problem.
You not need create new object. You have to call like this:
#Controller
#RequestMapping("/test")
public class TestController {
#Autowired
private DayTradeServiceImpl dayTradeService;
#GetMapping(value = "/get")
public void getTrades() {
dayTradeService.getDayTrades(...);
}
}
And set annotation #Service for DayTradeServiceImpl.
#Service
public class DayTradeServiceImpl implements DayTradeService {
#Autowired
private DayTradeRepository dayTradeRepository;
#Override
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens) {
// Several lines of code and some of them is trying to use dayTradeRepository.
}
}
Spring framework use inversion of control, which has container for beans. For detect beans use annotation like: #Service, #Component, #Repository.
I want to test a serviceImpl (UpdateServiceImpl), the test is like:
#SpringBootTest
public class UpdateUserServiceImplTests {
#Spy
UserRepository userRepository;
#Mock
private UserInfoUpdate userInfoUpdate;
#Autowired
UserRepository userRepository1;
#Spy
#InjectMocks
private UpdateUserServiceImpl updateUserServiceImpl;
#BeforeEach
public void setupMock() {
MockitoAnnotations.openMocks(this);
userInfoUpdate = new UserInfoUpdate();
}
#Test
public void makeUserInfoRight (){
try {
System.out.println(userInfoUpdate.getUsername());
User user = updateUserServiceImpl.makeUserInfoFull(userInfoUpdate);
User user_to_update = userRepository1.findById(userInfoUpdate.getUsername())
.orElseThrow(()
-> new UserNotFoundException("User not found by thisusername : " + "{" +
userInfoUpdate.getUsername() + "}"));
}
And the Method ( makeUserInfoFull() ) in updateUserServiceImpl to be tested is like:
#Service
public class UpdateUserServiceImpl implements UpdateUserService {
#Autowired
UserRepository userRepository;
#Autowired
InfoGuess infoGuess;
#Override
public User makeUserInfoFull(UserInfoUpdate userInfoUpdate)
throws UserNotFoundException {
User user_to_update =
userRepository.findById(userInfoUpdate.getUsername())
.orElseThrow(() -> new UserNotFoundException("User not found by this username : " + "{" + userInfoUpdate.getUsername() + "}"));
The repository is simple one:
#Service
public interface UserRepository extends JpaRepository<User, String> {
}
The JpaRepository interface works fine through #Autowired in test unit (generated userRepository1), but the interface in Method ( makeUserInfoFull() ) I tried #Spy to mock into the test, the Repository (for example: userRepository.findAll() ) keeps return null result.
Is that no way to use real data in the test through JpaRepositoy?
You have to mock that repository also as follow.
#Mock
UserRepository userRepository1;
Then it will be injected within that service class.
Then you can use mockito when call and return what you want.
Mockito.when(userRepository1.findById(anyString()).thenReturn(object)
I think it's not good to use real data on unit test, because:
The database will dirty of test data (contains positive and negative case)
It doesn't make any sense if whenever we have to update our code to avoid that the data is empty or already exist.
Relate with no.2, the unit test should be independent. Not depend on other test even database.
In my opinion, we have two options:
Use Mockito to mock another service such as database or even 3rd party integration. To make our unit test more independent and also faster than using #SpringBootTest
If we are really have to use database integration, you can use H2 Database for your unit test only. So in the real environment you are using Mysql or anything, and using H2 Database only for Unit Test
I write a web application with Spring Boot and now I'm facing with the following problem:
I have a following Service class:
#Service
class ExampleService {
#Autowired
ARepository aRepository;
#Autowired
BRepository bRepository;
#Autowired
CRepository cRepository;
}
All repository interfaces extends
JpaRepository<MatchingClass, Integer>
Now I would like to perform following crud operations for each repository:
public List<AClass> getAll() {
List<AClass> aElements = new List<>();
aRepository.findAll().forEach(x->aElements.add(x));
return aElements;
}
public AClass getOne(Integer id) { return aRepository.getOne(id);}
public void addOne(AClass aClass) { aRepository.save(aClass);}
public void deleteOne(Integer id) {aRepository.delete(id);}
}
How can I achieve it without repeating methods with different parameter types? I have a basic knowledge about generics in java, but I'm not sure using it is permitted in spring data and, actually how to accomplish it properly.
If your repository interfaces are already extending JpaRepository<T, ID> then you don't need the methods deleteOne, addOne, getOne you could use the methods in JpaRepository direclty.
For example simply call from your service the methods delete, save, findOne, etc:
aRepository.save(aClassEntity);
Check org.springframework.data.repository.CrudRepository.
I'm pretty new to layered architecture, + spring + hibernate
after reading some guides on how the class hierarchy is supposed to be -
i came up with this structure:
public interface GenericDAO {
public <T> T getItemById(long id, Class<T> c);
public <T> int save(T... objectsToSave);
public <T> int saveOrUpdate(T... objectsToSave);
public <T> int delete(T... objectsToDelete);
.
.
}
now all my other daos impls are using this generic dao as a private field in order to use its basic methods:
i.e:
#Repository
public class UserDAOImpl implements UserDao {
#Autowired
private GenericDAO dao;
#Override
public int deleteUser(User u) {
return dao.delete(u);
}
.
.
.
}
My services are like this :
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDao userDao;
#Transactional(readOnly = false)
public int deleteUser(User u) {
return userDao.deleteUser(u);
}
.
.
.
}
I don't get why i need a UserDaoImpl , CarDaoImpl, XDaoImpl in my project? it seems really redundant since all the XDaoImpls looks the same:
#Repository
public class UserDAOImpl implements UserDao {
#Autowired
private GenericDAO dao;
#Override
public int deleteUser(User u) {
return dao.delete(u);
}
.
.
.
}
#Repository
public class CarDAOImpl implements CarDao {
#Autowired
private GenericDAO dao;
#Override
public int deleteCar(Car c) {
return dao.delete(c);
}
.
.
.
}
#Repository
public class XDAOImpl implements XDao {
#Autowired
private GenericDAO dao;
#Override
public int deleteX(X c) {
return dao.delete(c);
}
.
.
.
}
I could just not create any XDaoImpl and just use the GenericDaoImpl instead and save alot of classes creation, no?
If ill need any complex actions like deleteUserCar(User u) i can just implement the logic in the service:
UserService {
public void deleteUserCar(User u) {
Car c = u.getCar();
CarService cs.deleteCar(c);
}
}
Am i missing something?
can anyone please offer an example that using only GenericDaoImpl instead of XDaoImpl will make me regret it?
thanks
Your service will later on invoke businesslogic instead of just passing methods to the DAO. It may validate values (e.g. does it exist and is it supposed to be unique), run calculations (e.g. int getStock() { return bought - sold; } and so on.
A generic DAO is great, though consider a abstract class instead of an interface. This way you don't need to create multiple create()s, just extend the abstract DAO (e.g. CarDAO extends AbstractDAO<Car>).
Your extended DAO will pass the class it handles to the generic abstract DAO (as seen in previous example).
Your extended DAO will later on implement extra methods that only apply on that particular object, e.g.: List<Car> getCarsWithColor(Color color).
Your Service -> DAO relationship is not always one-on-one. Consider these DAOs: TruckDAO, CarDAO, VanDAO with objects Truck extends Vehicle, Car extends Vehicle, Van extends Vehicle. Do you need three services, or will a VehicleService cover it (will you run logic for all Vehicles perhaps)?
Reconsider the use of interfaces, this question applies to C# but the concept is the same.
My advice is: Just keep it simple! Please don't create too much abstraction that leads to too much complexity, remember that if you create create so much code that you don't need, that code you will be forced to mantain: tons of trash that you don't even know what was its purpose, tons of code that obscure the real objectives of your application.
So in this particular case I advice to :
Forget about creating DAO interfaces: They are intended to abstract the DAO implementations in order to switch 'easily' from databases (eg. MySQL to SQLServer) but think!: that is a really rare thing: it is more common to switch systems than to switch databases
Put that GenericDao in the trash (The world is not about CRUDs, go to your stakeholders and ask what they really need)
Use a simple DAO Layer and Service Layer, you can implement that with Spring: I imagine
that you can use a SimpleJDBCTemplate in DAO Layer and call that DAO methods in Service layer
Why use Spring after all? Have you asked yourself what's its purpose?, Currently I work with MyBatis in this way: I create Mappers (something analogous to DAOs) that are called by POJO services. Simple, useful and straightforward, no Spring, plain old Java, works like champ.