Service layer getting complex and unmaintainable - java

In my application, I have a service layer, which is many REST web services that deal with the DAO layer to do CRUD operations on the entities which represent database tables. I am using Hibernate in my DAO layer. An example of my Service layer Java classes was:
#Path("/customers")
public class CustomerService extends SessionUtil implements Service {
public static CustomerDao customerDao = (CustomerDao) context.getBean("customerDAO");
public static CustomerDebtDao customerDebtDao = (CustomerDebtDao) context.getBean("customerDebtDAO");
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Customer> getAllCustomersService() {
return (ArrayList<Customer>) customerDao.getAllCustomers();
}
#GET
#Path("/{start}/{end}")
#Produces(MediaType.APPLICATION_JSON)
public List<Customer> getCustomerBatchService(#PathParam("start") int start, #PathParam("end") int end) {
ArrayList<Customer> customers = (ArrayList<Customer>) customerDao.getCustomerBatch(start, end);
return customers;
}
#GET
#Path("/count")
#Produces(MediaType.APPLICATION_JSON)
public int getTotalRowCountService() {
return (int) customerDao.getTotalRowCount();
}
#GET
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_JSON)
public Customer getCustomerService(#PathParam("customerId") int customerId) {
Customer customer = (Customer) customerDao.getCustomer(customerId);
return customer;
}
#POST
#Path("/create")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createCustomerService(Customer customer) {
customerDao.createCustomer(customer);
return response;
}
#DELETE
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_JSON)
public Response deleteCustomerService(#PathParam("customerId") int customerId) {
customerDao.deleteCustomer(customerId);
return response;
}
#PUT
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response editCustomerService(Customer customer) {
customerDao.editCustomer(customer);
return response;
}
...
}
An example of the DAO layer is:
public class CustomerDaoImpl extends JdbcDaoSupport implements CustomerDao {
#Autowired
private SessionFactory sessionFactory;
#Override
public void createCustomer(Customer customer) {
customer.setCustomerId(getNextCustomerId());
customer.setCreated(new Date());
customer.setCustomerId(getNextCustomerId());
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(customer);
try {
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
}
#Override
public void editCustomer(Customer customer) {
Session session = sessionFactory.openSession();
session.beginTransaction();
session.update(customer);
try {
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
}
}
The problem is, in the service layer, I sometimes make more than one call to the DAO layer, and as you see, each call is handled in one Hibernate transaction which causes that if one operation fails, the others won't be executed. For example, I am telling the code to create an invoice then update the debts of the customer. I could find that it created an invoice and did not update debts. I looked into a couple of books and they all said I should handle all operations in a single transaction and roll them back if anything fails. I am trying to do this, but it's causing me to almost remove the whole DAO layer, and the service layer is getting huge, unreadable and unmaintainable. An example is as follows:
#POST
#Path("/create")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createSalesInvoiceLineService(SalesInvoiceLine salesInvoiceLine) {
Session session = sessionFactory.openSession();
session.beginTransaction();
// prepare sales invoice line object
salesInvoiceLine = salesInvoiceLineDao.createSalesInvoiceLine(salesInvoiceLine);
session.save(salesInvoiceLine);
updateSalesInvoiceAmountForCreate(salesInvoiceLine, session);
// update stock
Stock stock = stockDao.getStockByProduct(salesInvoiceLine.getProductId());
stock.setQuantity(stock.getQuantity().subtract(salesInvoiceLine.getQuantity()));
stockDao.editStock(stock);
session.save(stock);
// update debt
SalesInvoice salesInvoice = salesInvoiceDao.getSalesInvoice(salesInvoiceLine.getSalesInvoiceId(), session);
List<CustomerDebt> customerDebtList = customerDebtDao.getCustomerDebtByCustomerId(salesInvoice.getCustomerId());
CustomerDebt customerDebt = customerDebtList.get(0);
customerDebt.setAmount(customerDebt.getAmount().add(salesInvoiceLine.getLineAmount()));
Date date = new Date();
java.sql.Date currentDate = new java.sql.Date(date.getTime());
customerDebt.setUpdateDate(currentDate);
customerDebtDao.editCustomerDebt(customerDebt);
session.update(customerDebt);
try {
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.close();
}
return response;
}

in addition to existing comments:
add pure service layer where you implement business logic using DAOs and use that layer via dependency injection in REST layer e.g.
#Path("/customers")
public class CustomerService {
private Service service;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Customer> getAllCustomersService() {
return (ArrayList<Customer>) service.getAllCustomers();
}
...
service layer can be easily tested, exposed to other transport layers(SOAP,..)...

Related

Cannot invoke "jakarta.persistence.EntityManager.createQuery(String, java.lang.Class)" because "this.entityManager" is null

Good afternoon, I am trying to get the data from my database. My application is with abse in microservices which has the following
This is my class for the database configuration.
#ApplicationScoped
public class DbConfig {
#Inject
#ConfigProperty(name = "db.connection.username")
private String dbUser;
#Inject
#ConfigProperty(name = "db.connection.password")
private String dbPassword;
#Inject
#ConfigProperty(name = "db.connection.url")
private String dbUrl;
#Produces
#ApplicationScoped
public EntityManager entityManager() {
Map<String, String> properties = new HashMap<>();
properties.put("javax.persistence.jdbc.url", dbUrl);
properties.put("javax.persistence.jdbc.user", dbUser);
properties.put("javax.persistence.jdbc.password", dbPassword);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence-books", properties);
return emf.createEntityManager();
}
}
This is my RepositoryImpl class book
#ApplicationScoped
public class BookRepositoryImpl implements BookRepository {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Book> findAll() {
try {
TypedQuery<Book> query = entityManager.createQuery("SELECT b FROM Book b ORDER BY b.id ASC", Book.class);
return query.getResultList();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
..// another CRUD methods
This is my rest class
#ApplicationScoped
#Path("/books")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class BookRest {
#GET
public List<Book> findAll() {
System.out.println("Buscando todos");
return bookService.findAll();
}
..// another CRUD Methods
}
The problem arises when I start the application through the server class, it starts correctly at localhost:7001, but in order to verify that it is working correctly I need to list the data in the database through localhost:7001/books, which gives me this error Cannot invoke "jakarta.persistence.EntityManager.createQuery(String, java.lang.Class)" because "this.entityManager" is null

How to create a MongoDB DAO layer like JPA using the entity manager does to facilitate all other DAO class in term of design?

Knowing that there are some frameworks to do it in real-world situation. As I learning from scratch, I wonder is that possible to create pure MongoDB DAO as a layer on top of the Service class to do CRUD operation that also facilitate other DAO use it?
For example, below is my Generic DAO class to operate CRUD process.
public interface IGenericDAO<T> {
public T create(T t);
public T update(T t);
public T get(Object id);
public void delete(Object id);
public List<T> listAll();
public Long count();
}
Then, DAO class should implements its operations
public class UserDAO implements IGenericDAO<User> {
MongoDatabase database = dBUtils.getMongoDB();
MongoCollection <Document> userTbl = database.getCollection("User");
public UserDAO() {
super();
}
#Override
public User create(User user) {
return user;
}
// other CURD below
}
User Service class
public class UserService {
private UserDAO userDAO;
public UserService() {
}
public void listUser() {
// need to test
List<User> listUsers = userDAO.listAll();
}
public void create(User user) {
// this what I want to see. user is saved to db here
try {
MongoDatabase database = dBUtils.getMongoDB();
assert mdb != null;
// create or get collection
MongoCollection<User> userTbl = database.getCollection("Users", User.class);
User userDoc = new User();
userTbl.insertOne(userDoc);
} catch (Exception e) {
e.printStackTrace();
}
}
Having said that, I want to put MongoDB "layer" between the DAO and Service class to do the CRUD operation. I wonder is it necessary, otherwise, how to do it to help the UserService with UserDAO class?

How to update object parameter in DAO?

I'm new at Java and trying to understand how MVC architecture goes. Forgive me if I'm wasting your time. I wrote a DAO Service, it handles the crud model (get, read, update, delete).
public List<User> getUsers();
public User getUser(Long userId);
public void createUser(User user);
public void updateUser(User user);
public void delete(Long userId);
}
here are my abstract DAO functions.
#Override
#Transactional
public void updateUser(User user) {
em.merge(user);
}
and the controller:
#PutMapping(value = "/{userId}", produces = "application/json")
public ResponseEntity<UserDTO> update(#PathVariable Long userId, #RequestBody UserDTO user){
try{
service.updateUser(user);
return new ResponseEntity<>(HttpStatus.OK);
} catch (HttpClientErrorException p){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} catch (HttpServerErrorException.InternalServerError u){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
in the service:
#Override
public void updateUser(UserDTO user) {
userDAO.updateUser(ApiDTOBuilder.userDTOToUser(user));
}
How can I pass the userId and set the new parameters into the current user object?
First you need to fetch the User using DAO method. And then you need to set the values to the User entity. As you are using #Transactional, spring will take care of updating the values.
#Override
#Transactional
public void updateUser(Long userId,UserDTO userdto) {
User user= getUser(userId);
user.setFirstName(userdto.getFirstName());
user.setLastName(userdto.getLastName())
}
Also if you are not using Spring Boot, then you need to enable transaction management by using
#EnableTransactionManagement

#Inject gives a NullPointer Exception in java EE

I'm trying to create a webservice that gives some results taken through hibernate from the database.
#Path("/book")
public class BookService {
#Inject
private dbController db;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getBookTitle() {
return "H2G2";
}
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
public Response getUsers(){
List<UserEntity> users = db.getUsers();
return Response.ok(users,MediaType.APPLICATION_JSON).build();
}
}
the db variable whenever I call http://localhost/book/users is always null.
The dbController is:
public class dbController {
#Inject
private HibernateUtil util;
public List<UserEntity> getUsers(){
List<UserEntity> result = null;
try{
result = (List<UserEntity>) this.util.createQuery("select e from UserEntity e");
}catch (Exception e){
System.out.println(e.getMessage());
}
return result;
}
}
and the HibernateUtil is:
public class HibernateUtil {
private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
private EntityManager entityManager = null;
private void createEntityManager(){
if (this.entityManager==null){
this.entityManager = entityManagerFactory.createEntityManager(); // here is your persistence unit name
}
}
private void closeConnection(){
this.entityManager.close();
this.entityManager = null;
}
public List createQuery(String query) throws Exception{
this.createEntityManager();
List result;
try{
result = this.entityManager.createQuery(query).getResultList();
}catch (Exception e){
throw new Exception(e.getMessage());
}
return result;
}
}
I'm using intellij and I added a break point at the db.getUsers() and set the variable db by adding new dbController(). However, the Intellij gives me the error "Class not loaded : controller.dbController".
The hibernate works for sure...so the problem is not there. It's the first time I'm trying to use Dependency Injection, but I'm not sure what I'm doing wrong.
Thanks
You cannot inject POJO it has to be a Bean. So making it a bean requires the annotations, for example:
#Stateful
#LocalBean
public class dbController {..} // Should be DbController, start with CAPS
and
#Stateful // or maybe #Stateless ?
#LocalBean
public class HibernateUtil {..}
Then when you have a Bean it is not allowed to use static final so change like this is needed:
private EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("NewPersistenceUnit");
But actually the easiest way to get EntityManager is just to inject it also. Like:
#PersistenceContext// maybe also with unit name (unitName = "whatever_the_unit_name_is")
private EntityManager em;

Use JAX-RS and JPA with Tomcat without catch-finally

We are working with JAX-RS and JPA. We use methods that have the following structure (details omitted):
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResultObject saveById( #PathParam("id") BigInteger id,
SomeObject someObject) {
entityManager = EMF.obtainEntityManager();
try {
.. start transaction
.. write all information to the database
.. commit transaction
.. return ResultObject
} catch ( Exception exception) {
.. rollback transaction
.. return ResultObject together with an appropriate error
} finally {
entityManager.close();
}
}
Is there a ‘best’ way to avoid repeating the catch and finally on every JAX-RS method that we create? Using filters?
Our service provicer only supports Tomcat. No Glassfish or other containers.
Thanks for any help.
I would move implementation to different service that would be called by REST service. REST should be a type of your API. And logic should be in different service.
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResultObject saveById( #PathParam("id") BigInteger id, SomeObject someObject) {
ResultObject resultObject = new ResultObject();
ResultType res = someService.saveById(id, someObject)
// do something with res
return resultObject ;
}
And then in SomeService would implement some abstract class that could implement a logic of transaction.
public abstract class AbstractService {
protected void startTransaction() {
//...
}
protected void endTransaction() {
//...
}
}
public class SomeService extends AbstractService {
public ResultType saveById(BigInteger id, SomeObject someObject) {
startTransaction();
// your logic
endTransaction();
}
}
There is also better way. You could use Spring Framework if you know it.
In that solution you annotate SomeService (or method in that class) with #Transactional.
#Transactional
public class SomeService {
public ResultType saveById(BigInteger id, SomeObject someObject) {
// your logic
}
}
Create EntityManger in a filter
Eg:-
public class EntityManagerFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
EntityManager em = null;
try {
/
em = EntityManagerFactoryUtil.entityManagerFactory.createEntityManager();
//
EntityManagerUtil.ENTITY_MANAGERS.set(em);
chain.doFilter(request, response);
EntityManagerUtil.ENTITY_MANAGERS.remove();
//
} catch (Exception ex) {
//
} finally {
try {
if (em != null) {
em.close();
//
};
} catch (Throwable t) {
//
}
}
}
public void init(FilterConfig config) {
destroy();
initEntityManagerFactory();
}
private void initEntityManagerFactory() {
EntityManagerFactoryUtil.entityManagerFactory =
Persistence.createEntityManagerFactory("PersistanceUnitName");
//
}
public void destroy() {
//
try {
if (EntityManagerFactoryUtil.entityManagerFactory != null) {
EntityManagerFactoryUtil.entityManagerFactory.close();
};
} catch (Exception t) {
/
}
}
public class EntityManagerUtil {
public static final ThreadLocal<EntityManager> ENTITY_MANAGERS = new ThreadLocal<EntityManager>();
/** Returns a fresh EntityManager */
public static EntityManager getEntityManager() {
return ENTITY_MANAGERS.get();
}
}
public class EntityManagerFactoryUtil {
public static EntityManagerFactory entityManagerFactory;
}

Categories

Resources