I am kinda confuse how to test my dao layer which contain same method as my service layer. I managed to test my service layer and stuck figuring out how to test my dao layer.
#Repository
public class DaoImpl implements Dao {
#Autowired
private NamedParameterJdbcTemplate template;
#Override
public Set<MyForm> findSomething(String title, String name, ZonedDateTime datetime) {
String sql= "SELECT * from myTable WHERE title= :d_title AND name = :d_name AND mydatetime= :d_datetime";
SqlParameterSource namedParameters = new MapSqlParameterSource()
.addValue("d_title", title)
.addValue("d_name",name)
.addValue("d_datetime",datetime.withZoneSameInstant(ZoneId.of("UTC")).toOffsetDateTime());
List<MyForm> myForm= template.query(sql, namedParameters, new MyRowMapper());
Set<MyForm> setMyForm = new HashSet<MyForm>(myForm);
return setMyForm ;
}
To test your datalayer you can make use of Spring Boots Testslice DataJdbcTest.
#DataJdbcTest
public class DaoImplIT {
#Autowired
private DaoImpl daoImpl;
#Test
void test() {
// prepare your database
// call the method to test
// make assertions about the result
}
}
In this testslice Spring Boot will configure all beans related to data access via JDBC. If you provide an in-memory DB (e.g. H2), it will be used automatically.
The main idea of this type of test is to prepare your database by inserting data and then use your repository beans to query them.
I wrote a blog post about this (okay, it's #DataJpaTest but the main idea is the same.). Maybe it will help you.
Related
In my application, I am fetching customer details from an API and saving this customer to the database. after saving this customer object to in my database, I am returning customer object with id generated by the database.
this is my rest Controller layer for getting customer object from API.
//add a new customer and then return all details of newly created customer
#PostMapping("/customer")
public Customer addCustomer(#RequestBody Customer theCustomer)
{
// also just in case they pass an id in JSON ... set id to 0
// this is to force a save of new item ... instead of update
theCustomer.setId(0);
return theCustomerService.saveCustomer(theCustomer);
}
this is my service layer
#Service
public class CustomerServiceImpl implements CustomerService {
private CustomerDAO theCustomerDAO;
// set up constructor injection
#Autowired
public CustomerServiceImpl(CustomerDAO theCustomerDAO)
{
this.theCustomerDAO=theCustomerDAO;
}
#Override
#Transactional
public Customer saveCustomer(Customer thCustomer) {
return theCustomerDAO.saveCustomer(thCustomer);
}
}
and this my CustomerDAO layer where I am saving it to database
public Customer saveCustomer(Customer theCustomer)
{
// get the current hibernate session
Session currentSession = entityManager.unwrap(Session.class);
//save the customer
currentSession.saveOrUpdate(theCustomer);
return theCustomer;
}
above parts of my Application are working properly but now I want to add testing in it.
so I created a test method for the service layer.
class CustomerServiceImplTest {
#Test
void saveCustomer() {
CustomerDAO theCustomerDAO=mock(CustomerDAO.class);
CustomerServiceImpl theCustomerServiceImpl=new CustomerServiceImpl(theCustomerDAO);
Customer inCustomer=new Customer("john","nick","google#gmail.com","CPOI939","8607574640");
inCustomer.setId(0);
Customer outCustomer=inCustomer;
outCustomer.setId(9);
when(theCustomerDAO.saveCustomer(inCustomer)).thenReturn(outCustomer);
assertEquals(outCustomer,theCustomerServiceImpl.saveCustomer(inCustomer));
}
}
But I am not sure that it's a good way of testing because we are not adding any business logic in the service layer.
so how do I test it and which layer should be tested.
Try to test this case on the integration level. There is no business logic just pure crud to save data to DB.
You can use DbUnit and in-memory databases like H2.
DbUnit is a JUnit extension targeted at
database-driven projects that, among other things, puts your database
into a known state between test runs.
example test:
#Test
#DatabaseSetup("sampleData.xml")
public void testSaveCustomer() throws Exception {
Customer inCustomer=new Customer("john","nick","google#gmail.com","CPOI939","8607574640");
theCustomerServiceImpl.saveCustomer(inCustomer)
List<Customer> customerList = customerService.findAll();
assertEquals(1, customerList.size());
assertEquals("john", customerList.get(0).getName());
...
}
More details Spring nad DBUnit
Is #Transactional support for NamedParameterTemplate.batchUpdate?
If something went wrong during the batch execution, will it rollback as expected? Personally, I am not experienced that. That's why I am asking.
Is there any document to check #Transactional supported methods.
public class JdbcActorDao implements ActorDao {
private NamedParameterTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
#Transactional
public int[] batchUpdate(List<Actor> actors) {
return this.namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
SqlParameterSourceUtils.createBatch(actors));
}
// ... additional methods
}
NamedParameterTemplate is just an abstraction around Jdbc. In spring it is the Transaction Manager that is responsible for managing transactions, not that you can not do it via plain JDBC but this is the spring way. Spring uses AOP internaly to inspect the annotated methods and delegates its transaction managment. But this role is separate from the NamedParameterTemplate.
So you can use it freely and annotate your methods as long as they are within a Spring managed component/bean with #Transactional
I want to execute sql statement inside my spring boot controller class with out defining any method in the jpa repository. The statement i want to use is
SELECT UUID();
This statement is database related and is not associated with a particular entity.
It would be nice if any one can provide solution for the execution of the above statement via
spring controller class
jpa repository (if recommended)
update
controller:
#Autowired
JdbcTemplate jdbcTemplate;
#RequestMapping(value = "/UUID", method = RequestMethod.GET)
public ResponseEntity<String> getUUID() {
String uuid = getUUID();
return buildGuestResponse(uuid);
}
public String getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid.toString();
}
You can use JdbcTemplate in your code.
The bean you will need in your configuration class is:-
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource)
{
return new JdbcTemplate(dataSource);
}
And the code to run the query is:-
#Autowired
private JdbcTemplate JdbcTemplate;
public String getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid.toString();
}
or may be like this:-
public UUID getUUID(){
UUID uuid = (UUID)jdbcTemplate.queryForObject("select UUID()", UUID.class);
return uuid;
}
This is generally architecturally bad design to execute any SQL (do any persistence) on presentation layer (controller or view) in JEE applications.
The best option is make controller to use service layer, when service layer calling the persistence layer for: obtaining, saving or updating data.
In any case, you can use Spring Data JDBC. Something like:
import org.springframework.jdbc.core.JdbcTemplate;
....
UUID uuid = (UUID)jdbcTemplate.query("SELECT UUID()", UUID.class);
....
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.
I have code.
#Repository
public class ArticlesDao {
#Autowired
private SessionFactory sessionFactory;
/**
* #param count Specifited how many article get from DB
* #param start Start offset. Default 0
* #return all get article
*/
#Transactional
public List<Article> getLastArticles(Integer count, Integer start) {
if (start == null) {
start = 0;
}
final Session currentSession = sessionFactory.getCurrentSession();
final Criteria criteria = currentSession.createCriteria(Article.class);
criteria.addOrder(Order.desc("publishedDate"));
criteria.setFirstResult(count + start);
criteria.setMaxResults(count);
return criteria.list();
}
}
And Controler
#Autowired
ArticlesDao dao;
#RequestMapping(value = "/")
public ModelAndView getHome(#RequestParam("page") int page) {
dao.getLastArticles("STH args");
}
My question is whether Handler getHome() should be annotated #Transactional?
No, you should not use #Transactional over the controller/ controller method.
#Transactional is better/correct to use in service layer or DAO.
Normally I use #Transactional in the service layer and not in the DAO, since I want the transaction to hold an operation of business value and not an elementary operation.
In controllers, as in your example there is no real code, but just delegation to a service method, where the transaction is started, so you don't need to start another transaction in the Controller.
It is a good point to start your declarative transactions on your service/bussiness layer.
Anyway I think that #Transactional should be used in the service layer, and, at the same time, in the integration layer. You can use
#Transactional(propagation=Propagation.REQUIRES_NEW)
in your service layer, and, at the same time,
#Transactional(propagation=Propagation.REQUIRED)
in your DAO. Your integration layer will be more independent, and more easily testeable in a transactional enviroment.
Only the service layer and not the DAO should know the transaction behaviour because this is part of the business logic.
Further in your case you could make your transaction read only with #Transactional(readOnly = true).