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