在Controller、Service和DAO层使用Java 8 Optional类处理NullPointerException

x33g5p2x  于2022-10-06 转载在 Java  
字(10.2k)|赞(0)|评价(0)|浏览(457)

1. 概述

在这篇文章中,我们将看到如何有效地使用Java 8 Optional类来处理实时项目中的空指针异常。如何在不同的层中处理空指针,如controller layer, service layer,DAO layer

关于Java 8Optional类的关键点:

*null references在历史上一直被引入编程语言中,一般用来表示没有一个值。

  • Java 8引入了java.util.Optional类来模拟一个值的存在或不存在。
  • 你可以用静态工厂方法Optional. empty, Optional.of, and Optional.ofNullable创建Optional objects。
  • Optional class支持许多方法,如map, flatMap, and filter,它们在概念上与流的方法相似。
  • 使用Optional 迫使你主动解开一个Optional,以处理没有值的情况;因此,你可以保护你的代码不发生意外的空指针异常。
  • 使用Optional 可以帮助你设计更好的API,在这些API中,用户只需阅读一个方法的签名,就可以知道是否需要一个Optional值。在这篇文章中,我们将看到如何在实时项目中使用Java 8的Optional性。
    让我们以使用Spring Hibernate Framework创建CRUD操作为例。

在典型的Spring MVC web application中,有三个层,即ControllerServiceDAO layer。

让我们看看Optional类API在每个层中的使用情况。

2. Optional Class API在DAO 层的用法

在DAO层,我们写代码从数据库中通过id or some字段检索记录,基本上,我们不确定该记录是否存在,在这种情况下,该方法返回空引用。让我们写一段代码来避免这种null pointer异常。

例子:

@Override
public Optional<User> getUserById(Integer userId) {
 return Optional.ofNullable((User) sessionFactory.getCurrentSession()
   .createCriteria(User.class).add(Restrictions.eq("userId", userId))
   .uniqueResult());
}

@Override
public Optional<User> loadUserByUsername(String userName) {
 Session session = sessionFactory.getCurrentSession();
 Criteria criteria = session.createCriteria(User.class)
   .add(Restrictions.eq("userName", userName))
   .add(Restrictions.eq("enabled", true));
 return Optional.ofNullable((User) criteria.uniqueResult());

}

请注意,使用Optional.ofNullable()静态工厂方法,如果存在就返回值,否则就返回一个空的Optional 实例。

3. Optional类API在Service Layer中的用法

让我们看看如何在Service Layer中使用这段代码。在Service Layer中,我们需要处理该记录是否存在。如果它存在,则返回对象,否则抛出一个异常或向客户报告错误。

@Override
@Transactional
public User loadUserByUsername(String userName) {
return userDao.loadUserByUsername(userId).orElseThrow(
   () -> new ResourceNotFoundException(Integer.valueOf(userId)));
}

@Override
@Transactional
public void deleteUser(Integer userId) throws BaseException, ResourceNotFoundException {

 if (userId == null) {
  throw new BaseException(" Request param can't be null . " + userId);
 }
 final User user = userDao.getById(userId)
   .orElseThrow(() -> new UnauthorizedRequestException("User not exist"));
 user.setEnabled(false);
 userDao.update(user);

}


@Override
@Transactional(readOnly = true)
public User getUserById(Integer userId) throws BaseException {

 if (userId == null) {
  throw new BaseException(" Request param can't be null . " + userId);
 }

 return userDao.getById(userId).orElseThrow(
   () -> new ResourceNotFoundException(Integer.valueOf(userId)));
}

请注意,使用orElseThrow()方法,如果存在则返回值,否则抛出一个异常。在上面的例子中,我们使用了Optional.orElseThrow()方法和一个lambda表达式。

4. Java 8 Optional API在Controller Layer中的用法

我们假设UserService interface被注入到UserController class中,那么我们可以直接在e1d34d1中使用UserService interface方法。

在这个例子中,注意isPresent()方法在Optional Class中的使用情况。

@RequestMapping(path = "/users/{userId}", 
 method = RequestMethod.GET, 
 produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody ResponseEntity<User> getUser(
  @PathVariable("userId") String userId) {
 Optional<User> user = userService.getUser(userId);
 if (user.isPresent()) {
  return ResponseEntity.ok().body(user.get());
 }
 return ResponseEntity.badRequest().build();
}

还有一个用例是,如果你已经使用了Generic Dao layer,那么Optional Class的API可以像这样使用。

@Override
public Optional<T> getById(final K id) {
 try {
  return Optional.ofNullable((T) getSession().get(getType(), id));
 } catch (final RuntimeException re) {
  LOGGER.error("get failed", re);
  throw re;
 }
}

5. 完整的源代码参考

让我们来创建一个用户的POJO类。

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.codehaus.jackson.annotate.JsonManagedReference;

@Entity
@Table(name = "user")
public class User {

 private int userId;
 private String userName;
 private String password;
 private boolean enabled;
 private String firstName;
 private String lastName;
 private Long dateCreated;
 private Long dateUpdated;
 private long lastAccess;
 private long lastLogin;
 private String email;

 public User() {

 }

 public User(int userId, String userName, String password, boolean enabled
   ) {
  super();
  this.userId = userId;
  this.userName = userName;
  this.password = password;
  this.enabled = enabled;
 }

 
 
 public User(String userName, String password, boolean enabled,
   String firstName, String lastName, Long dateCreated, Long dateUpdated,
   long lastAccess, long lastLogin, String email) {
  super();
  this.userName = userName;
  this.password = password;
  this.enabled = enabled;
  this.firstName = firstName;
  this.lastName = lastName;
  this.dateCreated = System.currentTimeMillis();
  this.dateUpdated = System.currentTimeMillis();
  this.lastAccess = lastAccess;
  this.lastLogin = lastLogin;
  this.email = email;
 }

 @Id
 @Column(name = "userId")
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 public int getUserId() {
  return userId;
 }

 public void setUserId(int userId) {
  this.userId = userId;
 }

 public String getUserName() {
  return userName;
 }

 public void setUserName(String userName) {
  this.userName = userName;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public boolean isEnabled() {
  return enabled;
 }

 public void setEnabled(boolean enabled) {
  this.enabled = enabled;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public long getLastAccess() {
  return lastAccess;
 }

 public void setLastAccess(long lastAccess) {
  this.lastAccess = lastAccess;
 }

 public long getLastLogin() {
  return lastLogin;
 }

 
 public void setLastLogin(long lastLogin) {
  this.lastLogin = lastLogin;
 }
 
 public Long getDateCreated() {
  return dateCreated;
 }

 public void setDateCreated(Long dateCreated) {
  this.dateCreated = dateCreated;
 }

 public Long getDateUpdated() {
  return dateUpdated;
 }

 public void setDateUpdated(Long dateUpdated) {
  this.dateUpdated = dateUpdated;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

}

创建UserService.java接口和它的实现类UserServiceImpl.java

public interface UserService {
 User saveOrUpdateUser(User user) throws BadRequestException;
 boolean checkUserExist(String userName);
 Optional<User> loadUserByUsername(String userName);
 void deleteUser(int userId) throws BaseException, ResourceNotFoundException;
 List<UserResponse> getUsers() throws BaseException;
 Optional<User> getUserById(Integer userId) throws BaseException;
}
@Service
public class UserService implements IUserService {

 @Inject
 private IUserDao userDao;

 @Inject
 private PasswordEncoder passwordEncoder;

 @Inject
 private RoleService roleService;

 @Override
 @Transactional(readOnly = false)
 public User saveOrUpdateUser(User user) throws BadRequestException {
  validateUser(user); 
  this.userDao.save(user);
  return user;
 }

 private void validateUser(UserRequest userRequest) throws BadRequestException {

  if (userRequest == null) {
   throw new BadRequestException(" User object can't be null.");
  }

  if (userRequest.getEmail() == null) {
   throw new BadRequestException(" User email can't be null.");
  }

  if (userRequest.getFirstName() == null) {
   throw new BadRequestException(" User First name can't be null.");
  }

  if (userRequest.getLastName() == null) {
   throw new BadRequestException(" User last name can't be null.");
  }

  if (userRequest.getPassword() == null) {
   throw new BadRequestException(" Password can't be null.");
  }
 }

 @Override
 @Transactional
 public boolean checkUserExist(String userName) {
  final long count = userDao.checkUserExist(userName);
  return count > 0;
 }

 @Override
 @Transactional
 public User loadUserByUsername(String userName) {
 return userDao.loadUserByUsername(userId).orElseThrow(
    () -> new ResourceNotFoundException(Integer.valueOf(userId)));
 }

 @Override
 @Transactional
 public void deleteUser(Integer userId) throws BaseException, ResourceNotFoundException {

  if (userId == null) {
   throw new BaseException(" Request param can't be null . " + userId);
  }
  final User user = userDao.getById(userId)
    .orElseThrow(() -> new UnauthorizedRequestException("User not exist"));
  user.setEnabled(false);
  userDao.update(user);

 }

 @Override
 @Transactional(readOnly = true)
 public List<UserResponse> getUsers() throws BaseException {

  final List<UserResponse> listOfUsers = userDao.getUsers();
  if (listOfUsers == null) {
   throw new BaseException("Users not exist in database");
  }
  return listOfUsers;
 }

 @Override
 @Transactional(readOnly = true)
 public User getUserById(Integer userId) throws BaseException {

  if (userId == null) {
   throw new BaseException(" Request param can't be null . " + userId);
  }

  return userDao.getById(userId).orElseThrow(
    () -> new ResourceNotFoundException(Integer.valueOf(userId)));
 }

}

让我们创建UserDao.java接口和它的实现UserDaoImpl.java

public interface UserDao {
 User saveOrUpdateUser(UserRequest userRequest) throws BadRequestException;
 boolean checkUserExist(String userName);
 Optional<User> loadUserByUsername(String userName);
 void deleteUser(int userId) throws BaseException, ResourceNotFoundException;
 List<UserResponse> getUsers() throws BaseException;
 Optional<User> getUserById(Integer userId) throws BaseException;
}
@Repository
public class UserDao implements UserDao{

 @Autowired
 private SessionFactory sessionFactory;
 
 @Override
 public void saveOrUpdateUser(User user) {
  Session session = sessionFactory.getCurrentSession();
  session.saveOrUpdate(user);
 
 }
 
 @Override
 public Optional<User> getUserById(Integer userId) {
  return Optional.ofNullable((User) sessionFactory.getCurrentSession()
    .createCriteria(User.class).add(Restrictions.eq("userId", userId))
    .uniqueResult());
 }
 
 @Override
 public Optional<User> loadUserByUsername(String userName) {
  Session session = sessionFactory.getCurrentSession();
  Criteria criteria = session.createCriteria(User.class)
    .add(Restrictions.eq("userName", userName))
    .add(Restrictions.eq("enabled", true));
  return Optional.ofNullable((User) criteria.uniqueResult());

 }

 @Override
 public long checkUserExist(String userName) {
  Session session = sessionFactory.getCurrentSession();
  Criteria criteria = session.createCriteria(User.class)
    .add(Restrictions.eq("userName", userName))
    .add(Restrictions.eq("enabled", true));
  criteria.setProjection(Projections.rowCount());
  return (Long) criteria.uniqueResult();
 }
 
 @Override
 public List<UserResponse> getUsers(){
  Session session = sessionFactory.getCurrentSession();
  final List<UserResponse> users = new ArrayList<>();
  
  // get users code here
  return users;
 }
 
 @Override
 public void deleteUser(User user){
  Session session = sessionFactory.getCurrentSession();
  session.delete(user);
 }

}

6. 总结

在这篇文章中,我们已经学会了如何使用Java 8的Optional类来处理NullPointerException 在不同的层,如controller, service and DAO层。

相关文章