JPA CRUD

  • 基础增删改查接口,往往不应该耗费过多的精力去重复造轮子。这里借助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基本接口完成。有兴趣的小伙伴还可以加上其他的公共接口。✌️✌️
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343