专栏首页企业平台构建springboot jpa搭建开发环境(一)

springboot jpa搭建开发环境(一)

随着spring4的出现,也为springboot奠定了基础,其实在了解spring4原理与一些扩展的同时,我们也就可以很方便搭建开发环境,而springboot就是使用了4中的一些新特性与功能,将我们搭建的过程进行了记录,同时通过一些特有的检测机制,实现各种环境的自由选择预搭配,将需要配置的功能模块全部优先实现,而作为开发者,需要做的就是选择。

这里用到springboot+jpa+layui来搭建一个后台管理模块,由于jpa是很纯粹的面向对象持久层标准,针对业务中大多是单表的操作非常适合,代码量会得到大量的简化,而且逻辑会比较清晰,同时内置很多接口能够为我们实现几乎所有的功能。

本次主要选用springboot1.x版本,同样是以maven项目为开端,如果是用idea,我们可以直接使用spring项目构建工具完成。

而我们需要引入的依赖主要的起始就两个:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

由于多模块项目中,parent的引入能有一个,所以不通过spring-boot-starter-parent来控制其版本信息,该有如下:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.10.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

之后同样,构建用户的增删改查模块,由实体类、dao、service、web几个层次组成:

由于hibernate属于jpa的一种实现,我们现在使用hibernate ddl语句自动实现数据库建表,但是前提是,表名与字段属性要定义完全,当然如何定义,之前在spring hibernate 中也有用到,完全一样的做法。

比如User实体:

@Data
@Entity
@Table(name = "USER")
public class User {

    @Id
    @Column(name = "USER_ID",length = 36)
    @GenericGenerator(name = "uuid",strategy = "uuid")
    @GeneratedValue(generator = "uuid")
    private String userId;

    @Column(name = "username",length = 24)
    private String username;

    @Column(name = "password",length = 56)
    private String password;

    @Column(name = "user_caption",length = 56)
    private String userCaption;

    @Column(name = "sex",length = 2)
    private String sex;

    @Column(name = "age",length = 3)
    private String age;

    @Column(name = "telephone",length = 16)
    private String telephone;

    @Column(name = "email",length = 56)
    private String email;

    @Column(name = "address",length = 128)
    private String address;

    @Column(name = "description",length = 256)
    private String description;
}

这里通过lombok插件减少代码量,需要注意的是,注解都是javax.persistence.*中的;

然后直接启动项目,对应的库中就会自动生成相应的表了。需要注意的是表字段顺序没有按照实体定义的先后,而是按照名称进行排序的。

下一步就是如何构建dao以及相关的服务了,其实在jpa中内置了很多接口,我们可以根据不同需要来进行实现,达到功能扩展的目的。

目前可用的接口有如下:
CrudRepository:实现基础的增删改查
PagingAndSortingRepository:对上一个接口的加强,实现分页排序
JpaRepository:对上一个接口的加强,同时添加excmole查询
JpaSpecificationExecutor:通过Criteria动态查询
Repository:标志接口

每一个接口都有相应的方法与使用场景,具体用到在看,目前按照我们的需要,只用继承JpaRepository就能完成基础的功能,代码很简单:

@Repository
public interface UserDao extends JpaRepository<User,String> {
}

具体的实现就不用我们再写了,这个已经由框架内部实现了,就好比mybatis定义接口而不用管实现,虽然实现方式上有些不同,但原理一样。

接下来就要写service层的逻辑了,由于对单表的操作无非就那几种,我们何不把这些操作全部抽象成共有方法,那样只用做简单的继承就可以实现90%的功能,不用重复做一些无意义的事。

所以构建一个BaseService:

public interface BaseService<R ,T> {

    T getById(Serializable id);

    T getOne(String property,Object value);

    List<T> getAll(Collection<Condition> conditions);

    List<T> getAll(T t);

    Pager<T> getPager(Pager pager,Collection<Condition> conditions,Collection<Order> orders);

    T save(T t);

    void saveBatch(Collection<T> ts);

    T updateById(T t);

    T saveOrUpdate(T t);

    void deleteById(Serializable id);

    void delete(String property,Object value);

    void delete(T t);

    void deleteAll(Collection<T> ts);

    boolean exist(Serializable id);

    boolean exist(T t);
}

那么如何实现这个service,并且作为公共服务可以实现代码复用,那么必须药用到一个东西,就是泛型,可以先看下实现如写的:

public abstract class BaseServiceImpl<R extends Repository<T,Serializable>,T> implements BaseService<R,T>{

    /**
     * 通过examle查询
     */
    protected JpaRepository<T,Serializable> repository;
    /**
     * 通过Specification查询
     */
    protected JpaSpecificationExecutor<T> specificationExecutor;

    protected abstract Class<T> getDomainClazz();

    @Resource
    public void setRepository(R r) {
        if(r instanceof JpaRepository){
            this.repository = (JpaRepository<T, Serializable>) r;
        }
        if(r instanceof JpaSpecificationExecutor){
            this.specificationExecutor = (JpaSpecificationExecutor<T>) r;
        }
    }

    @Override
    public T getById(Serializable id) {
        return repository.getOne(id);
    }

    @Override
    public T getOne(String property, Object value) {
        return repository.findOne(ConditionHelper.buildExample(getDomainClazz(),property,value));
    }

    @Override
    public List<T> getAll(Collection<Condition> conditions) {
        return specificationExecutor.findAll(ConditionHelper.buildSpecification(conditions));
    }

    @Override
    public List<T> getAll(T t) {
        return repository.findAll(Example.of(t));
    }

    @Override
    public Pager<T> getPager(Pager pager, Collection<Condition> conditions, Collection<Order> orders) {
        Pageable pageable = new PageRequest(pager.getPageIndex(),pager.getPageSize(),ConditionHelper.buildSort(orders));
        specificationExecutor.findAll(ConditionHelper.buildSpecification(conditions),pageable);
        return null;
    }

    @Override
    public T save(T t) {
        return repository.save(t);
    }

    @Override
    public void saveBatch(Collection<T> ts) {
        repository.save(ts);
    }

    @Override
    public T updateById(T t) {
        return repository.save(t);
    }

    @Override
    public T saveOrUpdate(T t) {
        return repository.save(t);
    }

    @Override
    public void deleteById(Serializable id) {
        repository.delete(id);
    }

    @Override
    public void delete(String property, Object value) {

    }

    @Override
    public void delete(T t) {
        repository.delete(t);
    }

    @Override
    public void deleteAll(Collection<T> ts) {
        repository.deleteInBatch(ts);
    }

    @Override
    public boolean exist(Serializable id) {
        return repository.exists(id);
    }

    @Override
    public boolean exist(T t) {
        return repository.exists(Example.of(t));
    }
}

可以看到,所有方法都是由Repository这个接口的子接口完成,具体实现有哪些上面提到过,现在主要使用,JpaRepository、JpaSpecificationExecutor,在dao中我们其实已经实现了这两个接口,那么为什么是这两个接口,因为JpaRepository包含了基本所有功能,而JpaSpecificationExecutor帮助我们更好的扩展功能。

其实在实现的过程中已经做了一些处理,比如分页查询、条件、排序相关参数的处理,这个处理方法因人而异,在这边主要是将查询条件封装成约定的对象,相关查询是基于此对象,然后如何构建,都是一套完整与匹配的构建过程。

其实到这里已经完成了出多了,现在在定义一个controller,同样作为基类共业务服务继承。

public class BaseController<S extends BaseService<?,T>,T> {

    @Autowired
    protected S s;

    @GetMapping("/{id}")
    public T get(@PathVariable String id){
        return s.getById(id);
    }

    @GetMapping
    public List<T> getAll(Collection<Condition> conditions){
        return  s.getAll(conditions);
    }

    @GetMapping(params = {"pageIndex","pageSize"})
    public Pager<T> getPager(Pager pager, Collection<Condition> conditions, Collection<Order> orders){
        return s.getPager(pager,conditions,orders);
    }

    @PostMapping
    public T saveOrUpdate(T t){
        return s.saveOrUpdate(t);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable String id){
        s.deleteById(id);
    }
}

之后就可以快速完成基础业务功能。

剩下还有很多模块没有完成,当然还有页面,之后会使用layui来实现。

后台代码:https://github.com/suspring/springboot-jpa-ms.git

下次将会在此基础上进行补充完善。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 高并发Java(2):多线程基础

    使用线程的原因是,进程的切换是非常重量级的操作,非常消耗资源。如果使用多进程,那么并发数相对来说不会很高。而线程是更细小的调度单元,更加轻量级,所以线程会较为广...

    用户5640963
  • 【JVM学习资料之虚拟机栈中都有什么?】

    Java虚拟机栈(Java Virtual Machine Stacks) 是线程私有的,它的生命周期与线程相同。虚拟机栈为虚拟机执行Java方法(也就是字节码...

    用户5640963
  • 【springMVC基础】spring获取bean的几种方法

    ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext....

    用户5640963
  • Protocol Buffers 开发者指南

    欢迎来到 protocol buffers 的开发者指南。protocol buffers 是一个语言中立,平台中立针对通讯协议,数据存储和其他领域中对结构化数...

    HoneyMoose
  • WebLogic第二版 CNVD-C-2019-48814/CVE-2019-2725

    该漏洞是由wls9_async_response组件导致,在反序列化处理输入信息时存在缺陷,攻击者可以发送精心构造的恶意 HTTP 请求,未授权的情况下远程执行...

    洛米唯熊
  • 高并发Java(3):Java内存模型和线程安全

    网上很多资料在描述Java内存模型的时候,都会介绍有一个主存,然后每个工作线程有自己的工作内存。数据在主存中会有一份,在工作内存中也有一份。工作内存和主存之间会...

    用户5640963
  • 【Solr基础】是什么

    Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了...

    用户5640963
  • 【Jedis配置】springSSM + Jedis连接池配置

    当一个类实现了Serializable接口(该接口仅为标记接口,不包含任何方法定义),表示该类可以序列化.序列化的目的是将一个实现了Serializable接...

    用户5640963
  • Android NDK编程(一)---NDK介绍及环境搭建

    NDK全称为Native Development Kit,是Android的一个工具开发包,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应...

    Vaccae
  • Android利用SurfaceView显示Camera图像爬坑记(六) -- 用OpenCV进行Canny边缘检测

    上一篇《Android利用SurfaceView显示Camera图像爬坑记(五) -- 在现有项目中加入NDK配置》中我们已经把NDK加入到SurfaceVie...

    Vaccae

扫码关注云+社区

领取腾讯云代金券