I am doing a REST web service with a SpringBoot network and Morphia DAO (for using MongoDB).
As with Hibernate in MySQL, I would like to use common entities, repositories, and endpoints, so that I just need to install my entities, inherit repositories and services, and use the generated CRUDs with REST calls.
It is almost done, but I ran into a problem with a general update of my objects with Morphia. All that I have seen so far is about manually setting up a query with fields that need to be changed; but in Hibernate mode, we just set up an Id field called persist (), and it will automatically recognize that it has changed and applied the changes to the database.
Here is the code.
BaseEntity.java
package org.beep.server.entity;
import org.mongodb.morphia.annotations.Entity;
@Entity
abstract public class BaseEntity {
public static class JsonViewContext {
public interface Summary {}
public interface Detailed extends Summary{}
}
protected String id;
public void setId(String id) {
this.id = id;
}
}
User.java ( )
package org.beep.server.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.*;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.mongodb.morphia.annotations.*;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.ws.rs.FormParam;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
@Entity
@Indexes(
@Index(value="identifier", fields=@Field("email"))
)
@Builder
@NoArgsConstructor
@AllArgsConstructor
final public class User extends BaseEntity {
@Id
@JsonView(JsonViewContext.Summary.class)
private String id;
@Getter @Setter
@JsonView(JsonViewContext.Summary.class)
@FormParam("email")
@Indexed
@Email
private String email;
@Getter
@JsonView(JsonViewContext.Detailed.class)
@FormParam("password")
@NotEmpty
private String password;
public void setPassword(String clearPassword) throws NoSuchAlgorithmException, InvalidKeySpecException {
PasswordEncoder encoder = new BCryptPasswordEncoder();
String hashedPassword = encoder.encode(clearPassword);
setHashedPassword(hashedPassword);
}
protected void setHashedPassword(String hashedPassword) {
this.password = hashedPassword;
}
public UserDetails toUserDetails() {
return new org.springframework.security.core.userdetails.User(
getEmail(),
getPassword(),
true,
true,
true,
true,
AuthorityUtils.createAuthorityList("USER")
);
}
}
EntityRepository.java ( , Morphia)
package org.beep.server.repository;
import org.beep.server.entity.BaseEntity;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.dao.BasicDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
public class EntityRepository<Entity extends BaseEntity> extends BasicDAO<Entity, ObjectId> {
@Autowired
protected EntityRepository(Datastore ds) {
super(ds);
}
}
UserRepository.java ( )
package org.beep.server.repository;
import org.beep.server.entity.User;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.dao.BasicDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository extends EntityRepository<User> {
@Autowired
protected UserRepository(Datastore ds) {
super(ds);
}
}
EntityService.java ( , Rest)
package org.beep.server.service;
import org.beep.server.entity.BaseEntity;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.repository.EntityRepository;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.query.Query;
import org.mongodb.morphia.query.UpdateOperations;
import java.util.List;
public abstract class EntityService<Entity extends BaseEntity, Repository extends EntityRepository> implements ServiceInterface<Entity> {
protected Repository repository;
public EntityService(Repository repository) {
this.repository = repository;
}
public Entity create(Entity entity) throws UserEmailAlreadyExistsException {
repository.save(entity);
return entity;
}
public void delete(String id) throws EntityNotFoundException {
}
public List<Entity> findAll() {
return repository.find().asList();
}
public Entity findOneById(String id) throws EntityNotFoundException {
return (Entity) repository.get(id);
}
public Entity update(String id, Entity entity) {
Entity oldEntity = (Entity) repository.get(id);
entity.setId(id);
repository.save(entity);
return entity;
}
}
UserService.java
package org.beep.server.service;
import org.beep.server.entity.Message;
import org.beep.server.entity.User;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.repository.UserRepository;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Key;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.ws.rs.BadRequestException;
import java.util.List;
import java.util.Optional;
@Service
public class UserService extends EntityService<User, UserRepository> {
@Autowired
public UserService(UserRepository repository) {
super(repository);
}
}
RestResource.java ( )
package org.beep.server.api.rest.v1;
import com.fasterxml.jackson.annotation.JsonView;
import org.beep.server.entity.BaseEntity;
import org.beep.server.entity.User;
import org.beep.server.entity.BaseEntity;
import org.beep.server.exception.EntityNotFoundException;
import org.beep.server.exception.UserEmailAlreadyExistsException;
import org.beep.server.service.EntityService;
import org.beep.server.service.ServiceInterface;
import org.beep.server.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
public class RestResource<Entity extends BaseEntity, Service extends EntityService> {
protected Service service;
protected RestResource() {
this.service = null;
}
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
@JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity create(@RequestBody @Valid Entity object) throws UserEmailAlreadyExistsException {
return service.create(object);
}
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
@JsonView(BaseEntity.JsonViewContext.Detailed.class)
User delete(@PathVariable("id") String id) throws EntityNotFoundException {
service.delete(id);
return new User();
}
@RequestMapping(method = RequestMethod.GET)
@JsonView(BaseEntity.JsonViewContext.Summary.class)
List<Entity> findAll() {
return service.findAll();
}
@RequestMapping(value = "{id}", method = RequestMethod.GET)
@JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity findById(@PathVariable("id") String id) throws EntityNotFoundException {
return service.findOneById(id);
}
@RequestMapping(value = "{id}", method = RequestMethod.PUT)
@JsonView(BaseEntity.JsonViewContext.Detailed.class)
Entity update(@PathVariable String id, @RequestBody @Valid Entity object) {
return service.update(id, object);
}
@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public void handleEntityNotFound(EntityNotFoundException ex) {
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public List<ObjectError> handleValidationFailed(MethodArgumentNotValidException ex) {
return ex.getBindingResult().getAllErrors();
}
}