I am using Spring Data JPA to process database calls. For this purpose, I have created:
An EmployeeRepository interface, which extends a JpaRepository<Employee, Long>
An EmployeeService, which defines three methods:
Employee saveEmployee(Employee employee);
Optional<Employee> getEmployee(Long id);
Long deleteEmployee(Long id);
An implementation of the EmployeeService:
#Override
public Employee saveEmployee(Employee employee) {
return employeeRepository.save(employee);
}
#Override
public Optional<Employee> getEmployee(Long id) {
return employeeRepository.findEmployeeById(id);
}
#Override
public Long deleteEmployee(Long id) {
employeeRepository.deleteById(id);
return id;
}
The issue is the following:
The get-methods work fine and can return an optional. The save-method, on the other hand, cannot return an optional. Apparently the JpaRepository returns an instance of the saved object upon calling save(). I would rather return an optional, since something could go wrong when saving the employee and in that case, I would like to throw an error - i.e. whenever the optional is not present, I throw an error.
The same holds for the delete-operation: What, for example, if I ask to delete an employee and pass in an id, which does not exist? I would like to catch this error and only then return the passed in id, if the delete operation was successfull. Which error would I have to catch for this purpose? Can someone explain this to me?
=================================================
update:
I have fixed the problem with the delete-call by simply checking if the given employee-id exists before calling `deleteById(id); if it doesn't, the service returns null, if it does, it return the id. The controller looks like this:
#DeleteMapping("/{id}")
public ResponseEntity<Long> deleteEmployee(#PathVariable Long id) {
Long deletedEmployeeId = employeeService.deleteEmployee(id);
if (deletedEmployeeId != null) {
return ResponseEntity.ok(deletedEmployeeId);
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST);
}
I am missing the DataAccessException, however. So, can it be that I would actually have to do the following:
#DeleteMapping("/{id}")
public ResponseEntity<Long> deleteEmployee(#PathVariable Long id) {
try {
Long deletedEmployeeId = employeeService.deleteEmployee(id);
if (deletedEmployeeId != null) {
return ResponseEntity.ok(deletedEmployeeId);
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST);
}
} catch (DataAccessException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
}
This looks a bit like an over-kill to be honest.
I am still a bit unsure how to deal with the save-call. Before I posted this question, my controller was simply doing the following:
#PostMapping
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee) {
return ResponseEntity.ok(employeeService.saveEmployee(employee));
}
What happens, if employeeService.saveEmployee(employee) throws a DataAccessException? Am I still returning a HTTP-status-code of 200, as I wrap the response in an ResponseEntity.ok() ?
If so, I would suggest to do the following:
#PostMapping
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee) {
try {
Employee savedEmployee = employeeService.saveEmployee(employee);
return ResponseEntity.ok(savedEmployee);
} catch (DataAccessException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Is this something people do? Or are DataAccessExceptions usually neglected as they are not expected?
The method "save" always returns you the same object you are going to save. Only by checking the "id", you can see if the object is saved or not. But if an error occurs in the database, an exception will be thrown and you can catch it by putting "employeeRepository.save(employee)" in a try-catch block. The same way you can do for deleteById
Related
I can’t understand how to handle the following error:
In the class CustomerService I delete the customer by id, and if such an id does not exist, then an error must be thrown! How can you do without an if else construct?
CustomerService:
// Delete customer
public void deleteCustomer(Long id){
Customer customer = customerRepository.getByIdAndUserRole(id, "customer");
customerRepository.delete(customer);
}
CustomerController:
// DELETE MAPPING
//
// Delete customer with ID
#DeleteMapping("/customers/{id}")
void deleteCustomer(#PathVariable Long id) {
customerService.deleteCustomer(id);
}
Try to use Controller Advice. Whenever a exception occur it will directly handled by the handler. No if/else or try/catch blocks will be required.
1) Create a class CustomerControllerHandler, annotate with #ControllerAdvice.
2) Now create methods with arguments having the type of Exception.
3) The methods will return the JSON/POJO/void you want.
4) Annotate the methods with #ExceptionHandler(Exception.class) and
#ResponseStatus(HttpStatus.BAD_REQUEST),
#ControllerAdvice
public class CustomerControllerHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void processException(Exception ex) {
}
}
You can try using this instead. It's the deleteById method for a CrudRepository (hope you're using that) and it throws IllegalArgumentException if it can't find a customer.
I assumed that with "error" you meant "exception" and then in the controller you can surround with a try-catch block like that:
try{
customerService.deleteCustomer(id);
} catch (IllegalArgumentException e) {
log.error("No customer id exists!", e);
// if you have no logger, then use System.out.println() at least
}
If you wanted instead to return an error to the caller, then change the data type from void to HttpResponse<String> and when catching an exception you can return HttpResponse<>("No customer exists with that id!", HTTP.BAD_REQUEST). Now the caller will get a 400 - bad request.
A nicer approach would be to catch the exception in the service itself and return a boolean to the controller (true if customer is deleted and false if couldn't delete / couldn't find one).
if you want to throw error then you will have to check a condition, that is there will be an if statement, but not necessarily an else is needed.
For instance, you can check response of delete and throw error according to below one.
if (deleteCount == 0) {
//throw error here
}
Assume that "Project" is POJO. In service layer of my project, I wrote a function that is get a row from table.
#Override
public ProjectDto getAProject(Long id) {
try {
Project project = projectRepository.getOne(id);
if (project==null) {
throw new IllegalArgumentException("Project not found");
} else {
return modelMapper.map(project, ProjectDto.class);
}
} catch (EntityNotFoundException ex) {
throw new IllegalArgumentException("Project not found");
}
}
The function is working fine with already exist id values. But if I give non-exist value, an exception occur like following. Looks like "getOne()" function don't throw "EntityNotFoundException".
ModelMapper mapping errors: Error mapping com.issuemanagement.entity.Project to com.issuemanagement.dto.ProjectDto
that means the exception come from model mapper logic. Because "project" object filled with null values so couldn't map to DTO class. I modified the function as following to fix this.
#Override
public ProjectDto getAProject(Long id) {
boolean isExist = projectRepository.existsById(id);
if (isExist) {
Project project = projectRepository.getOne(id);
return modelMapper.map(project, ProjectDto.class);
} else {
throw new IllegalArgumentException("Project not found");
}
}
but in this way the program goes to DB for two times. I don't like it. How can I do this operation with just one transaction?
BTW, if I try to run "toString()" function of "project", it throw "EntityNotFoundException" but it's looks like not official way. or it is? I hope there should be a boolean variable in somewhere.
getOne() on JpaRepository will call getReference() on EntityManager under the hood which will return an instance whose state is lazily fetch .The EntityNotFoundException will not throw immediately but will only be thrown when its state is accessed at the first time .
It is normally used in the case that when you need to configure a #ManyToOne relationship for an entity (Let say configure a Department for an Employee) but you just have the ID of the related entity.(e.g. DepartmentId) . Using getOne() allows you to get a Department proxy instance such that you do not really need to query the database to get the actual Department instance just for setting up its relationship for an Employee.
In your case , you should use findById() which will return an empty Optional if the instance does not exist:
#Override
public ProjectDto getAProject(Long id) {
Project project = projectRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("Project not found"));
return modelMapper.map(project, ProjectDto.class);
}
I am using org.appfuse.dao.hibernatepackage and I have used all the method in the GenericDaoHibernate<T,PK> class.
I found these methods
public List<T> getAll();
public List<T> getAllDistinct();
public List<T> search(String searchTerm);
public T get(PK id);
public boolean exists(PK id);
public T save(T object);
public void remove(T object);
public void remove(PK id);
public List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams);
public void reindex();
public void reindexAll(boolean async);
I have some model classes, services and methods.
Now I want to get list of object using some other fieled in the model class other than id(I have some common fields in many model classes).
I need to write similar methods in all the services and daos. So i was thinking is it possible to create a common method in generic dao.
The following I tried, but it didn't work.
public T getbyClientKey(Long clientkey) {
Session sess = getSession();
IdentifierLoadAccess byId = sess.byId(persistentClass);
List<T> entity = (List<T>) byId.load(clientkey);
if (entity == null) {
log.warn("Uh oh, '" + this.persistentClass + "' object with client '" + clientkey + "' not found...");
throw new ObjectRetrievalFailureException(this.persistentClass, clientkey);
}
return entity;
}
I knew this will be error. and it showed TypeCastingException, because return type of byId.load(id) is object only, not List.
So how can I create a method like that?
If so, I think I can create method for remove() also(But that's not necessary for me now, may be in future).
The Javadoc for IdentifierLoadAccess is pretty clear in how the load method should behave:
Return the persistent instance with the given identifier, or null if there is no such persistent instance.
This means it should return just one object, not a List of objects. Try casting it to T instead.
If you want to query your entity (that is, retrieve items by any other means than primary key), you most likely want to implement the search(String) method.
If you want to query your entity (that is, retrieve items by any other means than primary key), take a look at the UserDaoHibernate that is shipped with AppFuse. It contains a method loadUserByUsername() which is implemented like this:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List users = getSession().createCriteria(User.class).add(Restrictions.eq("username", username)).list();
if (users == null || users.isEmpty()) {
throw new UsernameNotFoundException("user '" + username + "' not found...");
} else {
return (UserDetails) users.get(0);
}
}
Obviously, if you want to return all items, it should be modified a bit (this one is made up):
public List<UserDetails> loadLockedUsers() {
List<UserDetails> users = (List<UserDetails>) getSession().createCriteria(User.class).add(Restrictions.eq("account_locked", true)).list();
return users;
}
I'm getting in trouble trying to separate the business and the persistance in my project.
The skeleton does more or less something like:
private UsuarioBO findById(String idUsuario) {
Usuario dao = getUsuarioDao().findById(idUsuario);
return new UsuarioBO(dao);
}
private void save(UsuarioBO bo){
Usuario dao = bo.bo2dao();
getUsuarioDao().save(dao);
}
Some clarifications about the code above:
UsuarioBO is a business objet and Usuario is an entity mapped to a DB
table.
The new UsuarioBO(dao) is just a mapping method and the
bo.bo2dao() is also a mapping method, but it creates a new empty entity
Usuario.
As you can see, the entity created in both methods is erased of memory on method finish.
The problem goes when i try to do this:
UsuarioBO example = findById("whatever");
save(example);
When i do this, hibernate tells me "there's an existing entity with the same identifier", and thats true! (It's creating an entity on findById() and another on save() ).
The only solution i've found is to use the entity as "bo". I mean, bring the entity object to the service, do whatever modifications on the entity directly, and when i finish do the save sending the entity in spite of a BO.
I'm pretty sure there's a better way of doing so, but how?
EDIT:
this is the save(dao) method:
public void save(Usuario usuario) throws Exception {
try {
getSession().saveOrUpdate(usuario);
} catch (RuntimeException e) {
//error treatment
throw e;
}
}
this is the bo2dao() method (its inside the BO bean):
public Usuario bo2dao() throws Exception {
Usuario dao = new Usuario();
try {
dao.setId(this.id);
dao.setPassword(this.password);
//other similar fields...
dao.setLastLoginTime(this.lastLoginTime);
Role r = new Role();
r.setId(LoginHelper.getRoleId(this.role.getName()));
dao.setRole(r);
Status s = new Status();
s.setId(LoginHelper.getStatusId(this.status.getName()));
dao.setStatus(s);
} catch (Exception e) {
throw e;
}
return dao;
}
In this case, Role and Status are also entity beans connected with Usuario (one user can only have one role and one status).
getRoleId() and getStatusId() returns the correspondent id from the name (for example: "UNLOCKED" returns 1)
How to use hql update query using hibernate template
thi is the hql statement "update Login set empSmartId = 48750005" +" where empPassword = 6328ef1675ddb7106eba8dc2661961d7"
using getHibernatetemplate()
Current code:
public class LoginDaoImp extends HibernateDaoSupport implements LoginDao {
public boolean resetAttempt(Login login) {
try { login.setAttempt(0);
getHibernateTemplate().update(login);
return true;
} catch (Exception e) { e.printStackTrace(); }
return false; }
i can save whole pojo class above code work but i want to use where condition to update only hibernateTemplate to update the data
you would be looking something like this
public class LoginDaoImp extends HibernateDaoSupport implements LoginDao
{
public boolean resetAttempt(Login login)
{
try
{
// you should create method for login to retrived based on password
//Remember getting login by password is not correct way, Instead you you should use primary key
//Getting persisted object of Login
Login persisted_login = getLoginByPassword(6328ef1675ddb7106eba8dc2661961d7);
//Setting value in persisted object of Login
persisted_login.setEmpSmartId (48750005);
getHibernateTemplate().update(persisted_login);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
I know this question is very old but, may this solution help someone else...
Here is a trick to update a record in DB.
The trick is first fetch the required record from the DB, update the required fields using setter methods of associated POJO class & then give a call to hibernateTemplate.save(Object entity) method, just like below:-
public void updateUser(User user, String password) {
#SuppressWarnings("unchecked")
List<User> results = (List<User>) hibernateTemplate
.find("FROM User where username = ?", new Object[] { new String(user.getUsername()) });
User userToBeUpdate = results.get(0);
userToBeUpdate.setPassword(password);
hibernateTemplate.save(userToBeUpdate);
}
This is working solution in my case.