SpringBoot Jpa企业开发示例详细讲解

Java
285
0
0
2023-06-20
标签   SpringBoot
目录
  • JPA 介绍
  • JPA 简单使用示例
  • JPA 实体类中相关注解说明
  • JPA 持久层关键字说明
  • 实战-JPA企业开发示例
  • 1. 实体继承基础通用属性
  • 2. 查询
  • 2.1 条件查询
  • 2.2 查询排序
  • 2.3 分页查询
  • 2.4 动态条件查询
  • 2.5 多表联查
  • 2.6 返回自定义Vo
  • 3. 修改和删除
  • 踩坑

JPA 介绍

JPA(Java Persistence API),对象关系映射(ORM)框架规范,是一种Java持久化规范。jpa可以通过实体类生成数据库的表,同时自带很多增删改查方法,大部分sql语句不需要我们自己写,配置完成后直接调用方法即可,很方便。

JPA 简单使用示例

1. pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
 <!-- mysql驱动 依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2. application.properties配置

# 数据库连接配置
spring.datasource.url = jdbc:mysql://localhost:3306/manageserver?serverTimezone=Asia/Shanghai
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
# jpa配置
# 自动更新数据库表
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
# 是否在控制台显示Hibernate的sql
spring.jpa.show-sql = false

3. 创建实体类对象,用于映射数据库表

@Table(name="db_dictionary")
@Data
@Entity
public class DbDictionary implements Serializable {
    @Id
    @GenericGenerator(name = "faceset_generator", strategy = "uuid")
    @GeneratedValue(generator = "faceset_generator")
    @Column
    private String id;
    /**
     * 类型名称
     */
    @Size(max =)
    @Column
    @NotNull
    private String type;
    /**
     * 中文
     */
    @Column
    @NotNull
    private String name;
    /**
     * 代码
     */
    @Column
    @NotNull
    private String code;
    /**
     * 顺序
     */
    private Integer sort;
    /**
     * 描述信息
     */
    @Column
    private String remark;
    private static final long serialVersionUID =L;
}

4. 创建持久化接口DAO,继承JpaRepository,用于对数据库进行操作

@Repository
public interface DictionaryDao extends JpaRepository<DbDictionary, String> {
}

5. 至此,JPA基本配置已经完成,这里自带很多内置的增删改查方法,不用我们自己写sql语句,直接调用即可。

JPA 实体类中相关注解说明

  • @Entity:表明是一个实体类
  • @Table(name = "dict_info"):对应的数据表名,与@Entity注解一块使用,但是如果表名和实体类名相同的话,@Table可以省略
  • @GenericGenerator(name = "jpa-uuid", strategy = "uuid"):自定义hibernate主键生成策略
  • @Id:表示当前字段为主键
  • @GeneratedValue(generator = "jpa-uuid"):指定主键生成策略为uuid
  • @Column(name = "DICTNAME"):当实体类的属性与其映射的数据库表的列不同名时需要使用@Column 标注说明
  • @CreatedDate:创建时间
  • @LastModifiedDate:更新时间
  • @Temporal(TemporalType.TIMESTAMP):实体类会封装成完整的时间“yyyy-MM-dd hh:MM:ss”的 Date类型
  • @Transient:表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性

JPA 持久层关键字说明

JPA Data的使用模式已经帮我集成了日常一些关键字用法,通过查询可以直接使用!

关键字

方法命名

sql where字句

And

findByNameAndPwd

where name= ? and pwd =?

Or

findByNameOrSex

where name= ? or sex=?

Is,Equals

findById,findByIdEquals

where id= ?

Between

findByIdBetween

where id between ? and ?

LessThan

findByIdLessThan

where id < ?

LessThanEquals

findByIdLessThanEquals

where id <= ?

GreaterThan

findByIdGreaterThan

where id > ?

GreaterThanEquals

findByIdGreaterThanEquals

where id > = ?

After

findByIdAfter

where id > ?

Before

findByIdBefore

where id < ?

IsNull

findByNameIsNull

where name is null

isNotNull,NotNull

findByNameNotNull

where name is not null

Like

findByNameLike

where name like ?

NotLike

findByNameNotLike

where name not like ?

StartingWith

findByNameStartingWith

where name like ‘?%’

EndingWith

findByNameEndingWith

where name like ‘%?’

Containing

findByNameContaining

where name like ‘%?%’

OrderBy

findByIdOrderByXDesc

where id=? order by x desc

Not

findByNameNot

where name <> ?

In

findByIdIn(Collection<?> c)

where id in (?)

NotIn

findByIdNotIn(Collection<?> c)

where id not in (?)

True

findByAaaTue

where aaa = true

False

findByAaaFalse

where aaa = false

IgnoreCase

findByNameIgnoreCase

where UPPER(name)=UPPER(?)

实战-JPA企业开发示例

1. 实体继承基础通用属性

/**
 * 实体继承映射类基础 ,保存实体的通用属性
 */
@Data
@Accessors(chain = true)
@MappedSuperclass   //实体继承映射
public abstract class BaseEntity implements Serializable {
    /**
     * 创建者
     */
    @Basic
    @Column(length =)
    private String creator;
    /**
     * 修改者
     */
    @Basic
    @Column(length =)
    private String modifier;
    /**
     * 创建时间
     */
    @Basic
    @Column(name = "create_time")
    @CreatedDate
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 修改时间
     */
    @Basic
    @Column(name = "update_time")
    @LastModifiedDate
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
}

2. 查询

2.1 条件查询

条件查询有两种方式,一是直接使用持久层提供的关键字,二是自己写查询SQL。

// 根据名称和性别查询用户
User findByNameAndSex(String name, String sex);
@Query(nativeQuery = true, value = "select * from user where name = :name and sex = :sex")
User findByNameAndSex(@Param("name") String name, @Param("sex") String sex);

2.2 查询排序

List<T> findAll(Sort sort);
//按创建时间倒序排列
Sort.by("createTime").descending()

2.3 分页查询

Page<T> findAll(Pageable pageable);
/*获取分页参数*/
public PageRequest getPageRequest() {
    return getPageRequest(Sort.unsorted());
}
/*获取分页和排序参数*/
public PageRequest getPageRequest(Sort sort) {
    ServletRequestAttributes attributes = getServletRequestAttributes();
    if (StringUtils.hasText(attributes.getRequest().getParameter("page")) && StringUtils.hasText(attributes.getRequest().getParameter("size"))) {
        int page = Integer.parseInt(attributes.getRequest().getParameter("page"));
        int size = Integer.parseInt(attributes.getRequest().getParameter("size"));
        return PageRequest.of(page, size, sort);
    }
    return PageRequest.of(Constants.zero, Constants.ten);
}

2.4 动态条件查询

动态条件查询Specification,DAO需要再继承JpaSpecificationExecutor接口

List<T> findAll(@Nullable Specification<T> spec);
Specification<ProjectionInfo> spec = (Specification<ProjectionInfo>) (root, query, cb) -> {
    List<Predicate> predicateList = new ArrayList<>();
    // 时间
    if (null != startTime && null != endTime) {
        predicateList.add(cb.between(root.get("createTime"), startTime, endTime));
    }
    // 数字
    if (searchDto.getCloud() != null) {
        predicateList.add(cb.le(root.<Integer>get("cloudPercent"), searchDto.getCloud()));
    }
    // 字符串
    if (searchDto.getName() != null) {
        predicateList.add(cb.equal(root.get("name"), searchDto.getName()));
    }
    // 列表
    if (StrUtil.isNotEmpty(searchDto.getSatellite())) {
        String[] satellites = searchDto.getSatellite().split(",");
        Expression<String> exp = root.<String>get("satellite");
        predicateList.add(exp.in(Arrays.asList(satellites)));
    }
    Predicate[] pre = new Predicate[predicateList.size()];
    pre = predicateList.toArray(pre);
    return query.where(pre).getRestriction();
};

2.5 多表联查

@Query(nativeQuery = true, value = "SELECT satellite, count(id), COALESCE(sum(file_size),) FROM t_mas_orbit_info" +
            " WHERE create_time BETWEEN :startTime AND :endTime " +
            " GROUP BY satellite")
List<Object[]> satelliteDataIncreased(@Param("startTime") Date startTime,
                                      @Param("endTime") Date endTime);

2.6 返回自定义Vo

@Query(value = "SELECT new cn.piesat.dispatch.model.vo.FileDataVO(p.id, p.fileName,p.stationCode,p.fileSize, p.fileDate, p.createTime, p.state, p.filePath,p.forwardTime) " +
            " FROM DbZxjProductInfoDTO p ")
List<FileDataVO> getFileList();

3. 修改和删除

进行修改和删除时,需要添加@Modifying、@Transactional注解。

@Query(nativeQuery = true, value = "UPDATE db_order_info SET download_count = (SELECT download_count +) WHERE id = :orderId ")
@Modifying
@Transactional
int updateDownloadTimes(String orderId);
@Modifying
@Transactional
void deleteByName(String name);

踩坑

1.删除的方法上一定要加@Transactional和@Modifying注解

2.自定义删除方法的时候如果传的是基本类型或者包装类型一定要用 void deleteByxxx(String s) 而不是 void deleteAllByxxx(String s)。

因为deleteAllByxxx(String s)会被jpa识别为查询语句,只有传入参数是列表时才是用deleteAllByxxx(List<String> s)

推荐使用原生sql,自带的delete或者deleteAll会先进行查询,然后在查询结果上面挨个执行删除,并不是一条sql执行完删除操作,所以还是建议自己写delete