公司最近的新项目使用的是JPA,迫于之前没有使用过,因此作成一个快速上手手顺

介绍

JPA 又叫 Spring Data JPA,是spring组件的一部分,相比 mybatis 来说,可以认为是一个全自动 ORM 框架

快速开始

创建一个springboot工程,引入web,mysql driver,jpa,lombok,test等依赖
pom如下:


pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

创建表的实体类:

package fun.psgame.jpa.demo.doman;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Getter
@Setter
@Entity
@Table(name = "tb_user")
public class User {
    @Id
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "address")
    private String address;
}

创建 Repository 类:

package fun.psgame.jpa.demo.dao;

import fun.psgame.jpa.demo.doman.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

创建controller进行测试:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @RequestMapping("/test")
    public Object test() {
        return this.userRepository.findAll();
    }
}

接着就可以使用 Repository 了。

进阶

查询

普通查询

根据方法名规则查询

关键词描述
find..By read..By get..By query..By search..By stream..By返回domain类型、集合类型、Streamable子类型或Optional类型
exists..By是否存在,返回boolean类型
count..By查询数量
delete..By remove..By无返回或者返回删除数量
..First<number> ..Top<number>..限制数量
..Distinct..结果去重

谓语修饰词有Distinct`AndOrIs/EqualBetween(Greater)LessThan(Equal)AfterBeforeIsNull/NullIsNotNull/NotNullStartingWithEndingWithContainingOrderByNotInNotInTrue`IngoreCase

添加方法是会有提示,如果IDEA没有提示请安装JPA Buddy插件

@Query查询

也可以添加自定义方法可以使用@Query来写sql,sql中可以用两种方式访问传过来的参数一种是?1`?2这种通过参数序号来访问的。另一种是通过具名参数,需要在参数上添加@Param("username"),然后通过:username`的方式使用。

查询时可以不写前面的select *,表名可以直接写类名

动态查询

Example方式

该方式只支持查询,并且只支持字符串参数,不能用Date之类的参数
(相比mybatis-plus差太多了)

Repository 继承QueryByExampleExecutor<T>

就有Example为参数的方法了

例子:

User user = new User();
user.setId(2L);
user.setUsername("柳岩");
user.setAddress("山东省青岛市");

ExampleMatcher matcher = ExampleMatcher.matching()
        .withIgnorePaths("id")
        .withMatcher("address",  ExampleMatcher.GenericPropertyMatchers.endsWith())
        .withMatcher("address",  m -> m.endsWith())
        .withStringMatcher(ExampleMatcher.StringMatcher.ENDING);

// 也可不使用matcher
Example<User> example = Example.of(user, matcher);

List<User> all = this.userRepository.findAll(example);

难用得一批,不推荐

Specification方式

Repository 继承JpaSpecificationExecutor<T>

例子:

Specification<User> specification = new Specification<User>() {
    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        // root 用来获取各个字段
        // criteriaBuilder 相当于where 用来添加各种条件
        // query 又来组合order by、where

        // 条件集合
        List<Predicate> list = new ArrayList<>();

        // 1
        Path<Object> idPath = root.get("id");
        Expression<Long> idExpression = idPath.as(Long.class);
        Predicate predicate = criteriaBuilder.greaterThan(idExpression, 1L);

        //2
        Predicate predicate1 = criteriaBuilder.like(root.get("username").as(String.class), "%柳岩%");

        list.add(predicate);
        list.add(predicate1);

        Predicate and = criteriaBuilder.and(list.toArray(new Predicate[0]));

        Order order = criteriaBuilder.desc(idExpression);

        // 条件 1 和 2
        return query.where(and).orderBy(order).getRestriction();
    }
};

List<User> userList = this.userRepository.findAll(specification);

这种方式还行,但是很难支持分组和聚合函数

ps:还可以使用entityManager方式获取lambda的三个参数:


手动使用jpa

首先注入:

@Autowired
private EntityManager entityManager;

然后:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = cb.createQuery();
Root<User> root = query.from(User.class);

这样也获取到三个参数了。

QueryDSL

需要引入额外的依赖,并且需要生成QUser类进行查询

多表查询

多表需要配置关联关系

使用@OneToOne`@OneToMany@ManyToMany@JoinColum`等注解配置关系

TODO

项目不用JPA了,暂时放置。


个人感觉JPA在多表关联方面有些弱,特别是写统计相关的sql时,不太好处理,更别说有时候是动态表名,比如按年份分表之后不太好处理,要么写 Hibernate 的插件,动态修改表(参考此文),要么引入sharding jdbc 进行处理。

标签: Java, Spring, Jpa

添加新评论