JPA CRUD

作者: 五洋捉鳖zz | 来源:发表于2020-09-07 14:01 被阅读0次
    • 基础增删改查接口,往往不应该耗费过多的精力去重复造轮子。这里借助JPA中已有的方法。在上层实现各类业务的功能。

    BaseEntity

    • 数据库表通用字段。比如每个表都有的主键,更新时间,创建时间,更新人,创建人,记录状态等。
    // @Getter,@Setter来自于 Lombok,如果不先还可以换成自生成的getter,setter。
    
    @Getter
    @Setter
    @MappedSuperclass
    @DynamicUpdate
    @DynamicInsert
    public abstract class BaseEntity implements Serializable {
    
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        protected Long id;
    
        @CreationTimestamp
        protected java.sql.Timestamp createTime;
    
        @UpdateTimestamp
        protected java.sql.Timestamp updateTime;
    
        private String createBy;
    
        private String updateBy;
    
        private Integer status;
    }
    
    

    Repo

    • Repository接口类这里不做封装。

    BaseService

    • 统一的service基类。这里的T就是EntityID代表主键类型。R代表Repository相关接口。
    public interface BaseService<T,ID, R extends JpaRepository<T, ID>> extends JpaRepository<T, ID>{
        // empty.
    }
    

    BaseServiceImpl

    • 实现BaseService的所有方法。其中save增加部分更新的功能。其余全部通过直接调用repository实现。
    public abstract class BaseServiceImpl<T extends BaseEntity, ID, R extends JpaRepository<T, ID>> implements BaseService<T, ID, R> {
    
        public  final R repo;
    
        public BaseServiceImpl(R repo) {
            this.repo = repo;
        }
    
        @Override
        public <S extends T> S save(S s) {
            T oldT = null;
            // 这里借助反射 & BeanUtils。copyProperties,实现类似mybatis plus updateSelective的效果,只更新非null字段。
            if(null != s.getId() && this.repo.existsById((ID)s.getId())) {
                Optional<T> t = this.repo.findById((ID)s.getId());
                if (t.isPresent()) {
                    oldT = t.get();
                    BeanUtils.copyProperties(oldT, s, genNotNullAttrs(s));
                }
            }
            return this.repo.save(s);
    
        }
    
        @Override
        public Optional<T> findById(ID id) {
            return this.repo.findById(id);
        }
    
        @Override
        public boolean existsById(ID id) {
            return this.repo.existsById(id);
        }
    
        @Override
        public long count() {
            return this.repo.count();
        }
    
        @Override
        public void deleteById(ID id) {
            this.repo.deleteById(id);
        }
    
        @Override
        public void delete(T t) {
            this.repo.delete(t);
        }
    
        @Override
        public List<T> findAll() {
            return this.repo.findAll();
        }
    
        @Override
        public List<T> findAll(Sort sort) {
            return this.repo.findAll(sort);
        }
    
        @Override
        public List<T> findAllById(Iterable<ID> iterable) {
            return this.repo.findAllById(iterable);
        }
    
        @Override
        public <S extends T> List<S> saveAll(Iterable<S> iterable) {
            return this.repo.saveAll(iterable);
        }
    
        @Override
        public void flush() {
            this.repo.flush();
        }
    
        @Override
        public <S extends T> S saveAndFlush(S s) {
            return this.repo.saveAndFlush(s);
        }
    
        @Override
        public void deleteInBatch(Iterable<T> iterable) {
            this.repo.deleteInBatch(iterable);
        }
    
        @Override
        public void deleteAllInBatch() {
            this.repo.deleteAllInBatch();
        }
    
        @Override
        public T getOne(ID id) {
            return this.repo.getOne(id);
        }
    
        @Override
        public <S extends T> List<S> findAll(Example<S> example) {
            return this.repo.findAll(example);
        }
    
        @Override
        public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
            return this.repo.findAll(example, sort);
        }
    
        @Override
        public Page<T> findAll(Pageable pageable) {
            return this.repo.findAll(pageable);
        }
    
        @Override
        public void deleteAll(Iterable<? extends T> iterable) {
            this.repo.deleteAll(iterable);
        }
    
        @Override
        public void deleteAll() {
            this.repo.deleteAll();
        }
    
        @Override
        public <S extends T> Optional<S> findOne(Example<S> example) {
            return this.repo.findOne(example);
        }
    
        @Override
        public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
            return this.repo.findAll(example, pageable);
        }
    
        @Override
        public <S extends T> long count(Example<S> example) {
            return this.repo.count(example);
        }
    
        @Override
        public <S extends T> boolean exists(Example<S> example) {
            return this.repo.exists(example);
        }
    
    
        public  String[] genNotNullAttrs(Object c) {
            List<String> res = new ArrayList<>();
            Field[] fields = c.getClass().getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                field.setAccessible(true);
                try {
                    if (null != field.get(c)) {
                        res.add(name);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            Field[] fields1 = c.getClass().getSuperclass().getFields();
            for (Field field : fields1) {
                String name = field.getName();
                field.setAccessible(true);
                try {
                    if (null != field.get(c)) {
                        res.add(name);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return res.toArray(new String[0]);
        }
    }
    

    BaseController

    restful接口基类。

    • T: entity实体类型注入。
    • ID: entity中主键类型注入。
    • VO: api接口 更新/插入 参数类.
    • DTO: entity对应的接口返回类.
    • R : repository系列。
    • S: 业务相关的service.

    VO,DTO: VO与DTO可能与entity实际相差不大。也可以说是entity的一种变形,因为某些情况下,数据库中的数据字段值是无法直接提供给前端展示的。又或者前端返回的数据字段无法直接存储到数据库中,必须做相应的转换。一般情况应该包含两种VO(列表查询VO,插入/更新VO), 及两种DTO(列表DTO, 详情返回DTO)。当然也有不用VO,DTO直接使用Entity的,没什么谁好谁坏,具体视情况&业务复杂程度而定。

    public abstract class BaseController<T, ID, VO, DTO,R extends JpaRepository<T,ID>,  S extends BaseService<T,ID, R>>{
    
        final S service;
    
        public BaseController(S service) {
            this.service = service;
        }
    
        @PostMapping("save")
        public ResponseEntity<DTO> save(@RequestBody VO vo)  {
            T data = this.service.save(this.persistence(vo));
            return new ResponseEntity<>(this.generate(data), HttpStatus.OK);
        }
    
        @GetMapping("del/{id}")
        public ResponseEntity<String> del(@PathVariable("id") ID id) {
            this.service.deleteById(id);
            return new ResponseEntity<>("ok", HttpStatus.OK);
        }
    
        @GetMapping("list")
        public ResponseEntity<List<DTO>> list() {
            List<T> data = this.service.findAll();
            List<DTO> res = new ArrayList<>();
            data.forEach(singleData -> {
                res.add(this.generate(singleData));
            });
            return new ResponseEntity<>(res, HttpStatus.OK);
        }
    
        @GetMapping("detail/{id}")
        public ResponseEntity<DTO> detail(@PathVariable ID id) throws YourCustomException {
            Optional<T> data = this.service.findById(id);
            if (! data.isPresent()) {
                throw new YourCustomException("the record does not exist!");
            }
            return new ResponseEntity<>(this.generate(data.get()), HttpStatus.OK);
        }
    
        // 将前端的返回的VO转换为数据库存储的entity
        public abstract T persistence(VO vo);
    
        // 将数据库返回的entity,转换为DTO
        public abstract DTO generate(T t);
    
        // 更新基础字段。例如更新操作,应当更新 ‘更新时间’,‘更新人员’,
        // 插入操作应当更新 ‘更新时间’, ‘更新人员’,‘创建时间’, ‘创建人员’ 
        public void updateBaseField(BaseEntity entity) {
            if (null == entity.getId()) {
                entity.insertInitialize();
            } else {
                entity.updateInitialize();
            }
        }
    }
    
    • 借助上述基类。实现一个简单的业务功能的CURD

    实现User业务模块

    UserEntity

    @Getter
    @Setter
    @Entity
    @Table(name = "your_user_table_name")
    public class UserEntity extends BaseEntity {
        private String userName;
        // ...省略其他attr.
    }
    

    UserRepository

    public interface UserRepository extends JpaRepository<UserEntity, Long> {
    }
    

    UserSerivce

    public interface UserService  extends BaseService<UserEntity, Long, UserRepo> {
        // 这里可以实现除了crud之外的其他复杂业务逻辑接口
        // .....
    }
    

    UserServiceImpl

    @Service
    public class UserServiceImpl extends BaseServiceImpl<UserEntity, Long, UserRepo> implements UserService {
    
        @Autowired
        public UserServiceImpl(UserRepo repo) {
            super(repo);
        }
    }
    

    UserVO

    public class UserVO{
    
        private String userName;
        
        private Long userId;
    }
    

    UserDTO

    public class UserDTO {
        
        private String userName;
    
        private String userid;
    }
    

    UserController

    @RestController
    @RequestMapping("/ your_context_path/userInfo")
    public class UserController extends BaseController<UserEntity, Long, UserVO, UserDTO, UserRepo, UserService>{
    
    
        @Autowired
        public UserController(UserSerivce service) {
            super(service);
        }
    
        @Override
        public UserEntity persistence(UserVO vo) {
            // vo -> entity的赋值,转换
           return new UserEntity();
        }
    
        @Override
        public UserDTO generate(UserEntity data) {
            // entity -> DTO的转换赋值。
            return new UserDTO();
        }
    }
    
    
    • 到此处,整个User模块的CRUD基本接口完成。有兴趣的小伙伴还可以加上其他的公共接口。✌️✌️

    相关文章

      网友评论

          本文标题:JPA CRUD

          本文链接:https://www.haomeiwen.com/subject/tookektx.html