22import org.springframework.stereotype.Service;
23import org.springframework.transaction.annotation.Transactional;
24
25@Service 26@Transactional(readOnly = true) 27@RequiredArgsConstructor 28public class UserService implements UserDetailsService { 29 30 private final UserRepo userRepo; 31 private final UserEditMapper userEditMapper; 32 private final UserViewMapper userViewMapper; 33 private final PasswordEncoder passwordEncoder; 34 35 @Transactional 36 public UserView create(CreateUserRequest request) { 37 if (userRepo.findByUsername(request.username()).isPresent()) { 38 throw new ValidationException("Username exists!"); 39 } 40 if (!request.password().equals(request.rePassword())) { 41 throw new ValidationException("Passwords don't match!"); 42 } 43 44 var user = userEditMapper.create(request); 45 user.setPassword(passwordEncoder.encode(request.password())); 46 47 user = userRepo.save(user); 48 49 return userViewMapper.toUserView(user); 50 } 51 52 @Transactional 53 public UserView update(ObjectId id, UpdateUserRequest request) { 54 var user = userRepo.getById(id); 55 userEditMapper.update(request, user); 56 57 user = userRepo.save(user); 58 59 return userViewMapper.toUserView(user); 60 } 61 62 @Transactional 63 public UserView upsert(CreateUserRequest request) { 64 var optionalUser = userRepo.findByUsername(request.username()); 65 66 if (optionalUser.isEmpty()) { 67 return create(request); 68 } else { 69 UpdateUserRequest updateUserRequest = 70 new UpdateUserRequest(request.fullName(), request.authorities()); 71 return update(optionalUser.get().getId(), updateUserRequest); 72 } 73 } 74 75 @Transactional 76 public UserView delete(ObjectId id) { 77 var user = userRepo.getById(id); 78 79 user.setUsername( 80 user.getUsername().replace("@", String.format("_%s@", user.getId().toString()))); 81 user.setEnabled(false); 82 user = userRepo.save(user); 83 84 return userViewMapper.toUserView(user); 85 } 86 87 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 88 return userRepo 89 .findByUsername(username) 90 .orElseThrow( 91 () -> 92 new UsernameNotFoundException( 93 format("User with username - %s, not found", username))); 94 } 95 96 public boolean usernameExists(String username) { 97 return userRepo.findByUsername(username).isPresent(); 98 } 99100 public UserView getUser(ObjectId id) {101 return userViewMapper.toUserView(userRepo.getById(id));102 }103104 public List<UserView> searchUsers(Page page, SearchUsersQuery query) {105 List<User> users = userRepo.searchUsers(page, query);106 return userViewMapper.toUserView(users);107 }108}
16import org.springframework.transaction.annotation.Transactional;
17import org.springframework.util.CollectionUtils;
18
19@Service20@Transactional(readOnly = true)21@RequiredArgsConstructor22public class BookService {2324 private final BookRepo bookRepo;25 private final AuthorRepo authorRepo;26 private final BookEditMapper bookEditMapper;27 private final BookViewMapper bookViewMapper;2829 @Transactional30 public BookView create(EditBookRequest request) {31 var book = bookEditMapper.create(request);3233 book = bookRepo.save(book);34 updateAuthors(book);3536 return bookViewMapper.toBookView(book);37 }3839 @Transactional40 public BookView update(ObjectId id, EditBookRequest request) {41 var book = bookRepo.getById(id);42 bookEditMapper.update(request, book);4344 book = bookRepo.save(book);45 if (!CollectionUtils.isEmpty(request.authorIds())) {46 updateAuthors(book);47 }4849 return bookViewMapper.toBookView(book);50 }5152 private void updateAuthors(Book book) {53 var authors = authorRepo.findAllById(book.getAuthorIds());54 authors.forEach(author -> author.getBookIds().add(book.getId()));55 authorRepo.saveAll(authors);56 }5758 @Transactional59 public BookView delete(ObjectId id) {60 var book = bookRepo.getById(id);6162 bookRepo.delete(book);6364 return bookViewMapper.toBookView(book);65 }6667 public BookView getBook(ObjectId id) {68 var book = bookRepo.getById(id);69 return bookViewMapper.toBookView(book);70 }7172 public List<BookView> getBooks(Iterable<ObjectId> ids) {73 var books = bookRepo.findAllById(ids);74 return bookViewMapper.toBookView(books);75 }7677 public List<BookView> getAuthorBooks(ObjectId authorId) {78 var author = authorRepo.getById(authorId);79 return bookViewMapper.toBookView(bookRepo.findAllById(author.getBookIds()));80 }8182 public List<BookView> searchBooks(Page page, SearchBooksQuery query) {83 return bookViewMapper.toBookView(bookRepo.searchBooks(page, query));84 }85}
14import org.springframework.stereotype.Service;
15import org.springframework.transaction.annotation.Transactional;
16
17@Service18@Transactional(readOnly = true)19@RequiredArgsConstructor20public class AuthorService {2122 private final AuthorRepo authorRepo;23 private final BookRepo bookRepo;24 private final AuthorEditMapper authorEditMapper;25 private final AuthorViewMapper authorViewMapper;2627 @Transactional28 public AuthorView create(EditAuthorRequest request) {29 var author = authorEditMapper.create(request);3031 author = authorRepo.save(author);3233 return authorViewMapper.toAuthorView(author);34 }3536 @Transactional37 public AuthorView update(ObjectId id, EditAuthorRequest request) {38 var author = authorRepo.getById(id);39 authorEditMapper.update(request, author);4041 author = authorRepo.save(author);4243 return authorViewMapper.toAuthorView(author);44 }4546 @Transactional47 public AuthorView delete(ObjectId id) {48 var author = authorRepo.getById(id);4950 authorRepo.delete(author);51 bookRepo.deleteAll(bookRepo.findAllById(author.getBookIds()));5253 return authorViewMapper.toAuthorView(author);54 }5556 public AuthorView getAuthor(ObjectId id) {57 return authorViewMapper.toAuthorView(authorRepo.getById(id));58 }5960 public List<AuthorView> getAuthors(Iterable<ObjectId> ids) {61 return authorViewMapper.toAuthorView(authorRepo.findAllById(ids));62 }6364 public List<AuthorView> getBookAuthors(ObjectId bookId) {65 var book = bookRepo.getById(bookId);66 return authorViewMapper.toAuthorView(authorRepo.findAllById(book.getAuthorIds()));67 }6869 public List<AuthorView> searchAuthors(Page page, SearchAuthorsQuery query) {70 return authorViewMapper.toAuthorView(authorRepo.searchAuthors(page, query));71 }72}
60 Optional<User> findByUsername(String username);
61}
62
63interface UserRepoCustom { 64 65 List<User> searchUsers(Page page, SearchUsersQuery query); 66} 67
68@RequiredArgsConstructor
69class UserRepoCustomImpl implements UserRepoCustom {
65 List<User> searchUsers(Page page, SearchUsersQuery query);
66}
67
68@RequiredArgsConstructor 69class UserRepoCustomImpl implements UserRepoCustom { 70 71 private final MongoTemplate mongoTemplate; 72 73 @Override 74 public List<User> searchUsers(Page page, SearchUsersQuery query) { 75 var operations = new ArrayList<AggregationOperation>(); 76 77 var criteriaList = new ArrayList<Criteria>(); 78 if (StringUtils.hasText(query.id())) { 79 criteriaList.add(Criteria.where("id").is(new ObjectId(query.id()))); 80 } 81 if (StringUtils.hasText(query.username())) { 82 criteriaList.add(Criteria.where("username").regex(query.username(), "i")); 83 } 84 if (StringUtils.hasText(query.fullName())) { 85 criteriaList.add(Criteria.where("fullName").regex(query.fullName(), "i")); 86 } 87 if (!criteriaList.isEmpty()) { 88 Criteria userCriteria = new Criteria().andOperator(criteriaList.toArray(new Criteria[0])); 89 operations.add(match(userCriteria)); 90 } 91 92 operations.add(sort(Sort.Direction.DESC, "createdAt")); 93 operations.add(skip((page.number() - 1) * page.limit())); 94 operations.add(limit(page.limit())); 95 96 var aggregation = newAggregation(User.class, operations); 97 var results = mongoTemplate.aggregate(aggregation, User.class); 98 return results.getMappedResults(); 99 }100}
This class does not have any documentation.
Consider adding a documentation comment to explain its use.
While it may seem like the functionality of a class is perfectly obvious, any consumers of your API may not be able to pick up on certain details.
Consider a case where the class given below can be instantiated and provides certain functionalities within each instance in a thread-safe manner, perhaps it is a rest API client.
If there is no documentation comment on the class, it is not immediately obvious that the class is thread safe. Thus, multiple instances of the class may be created to perform operations concurrently, using up both memory as well as OS resources like sockets. If it were known from the beginning that the class were thread safe, the user would not need to create unnecessary extra instances of SomeClass
.
class SomeClass {
// ...
}
Make sure to add useful information regarding the usage or implementation of a particular declaration, so that anything about it which can't be understood from the name or some other cue is correctly conveyed.
/**
* Instances of this class are used to perform xyz action.
*
* This class is thread safe and the same instance can be used over multiple threads.
*/
class SomeClass {
// ...
}
This issue will not be reported for model entity classes. If there is any non-obvious behavior associated with a particular class however, do consider documenting it.