Spring Data JPA 基础(一)

一. 概述

Spring Data JPA 是 Java Persistence API (JPA) 规范的实现,底层是对Hibernate 5.x 操作数据库的封装,它简化了在java开发中使用 JPA 访问数据库的操作。

二. 使用 Spring Data Repositories

Spring Data repository 抽象的目的就是显著减少各种数据访问层实现技术的样板代码:

// Spring Data repository 是最基础的接口,用于获取repository管理的domain对象的类型以及id的类型
// 注意泛型的使用
public interface Repository<T, ID extends Serializable> {

}

CrudRepository接口继承了Repository接口,并提供了它所管理的domain对象的基本的 CRUD 方法,新版本中还添加了很多新的api,具体信息可以查询api:

// domain对象CRUD操作的泛型接口
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    // 保存一个实体对象 insert into...
    <S extends T> S save(S entity);
    // 根据id获取domain对象,select * from xxx where id = ?
    Optional<T> findById(ID primaryKey);
    // 查询表中所有记录,将返回的结果集封装到一个集合对象中(List),select * from xxx
    Iterable<T> findAll();
    // 统计表中记录数
    long count();
    // 删除记录
    void delete(T entity);
    
    // ... lookup api for more details
}

PagingAndSortingRepository接口继承了CrudRepository接口并添加了分页查询的方法:

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    // Pageable对象封装了分页查询的参数,用于SQL中limit子句的查询参数 page & size
    Page<T> findAll(Pageable pageable);
}

JpaRepository接口又继承了PagingAndSortingRepository,开发中用户自定义repository可以直接继承该接口,同时JpaRepository还继承了一个QueryByExampleExecutor接口,该接口提供对QBE查询方法的支持,比较非主流。

根据 JPA 规范,项目中的领域对象要添加注解,用于建立domain和数据库表之间的静态映射关系:

@Entity
@Table(name = "tb_user")
public class User implements Serializable {
	// id可以设置生成策略,这里选择通过id生成器手动设置
	@Id
	private String id;
	@Column	// 属性名称如果和表中字段名称一致可以不加注解
	private String username;
	@Column
	private String password;
	@Column
	private String email
	@Column
	private Date birthday;

	// ... constructor & methods
}

三. JPA Repositories

在使用 JPA 时Query对象的获取有两种方法,一种是手写SQL,另一种是从方法名称中派生(神奇)。从方法名称中派生的方式适用于查询条件不太复杂的情况,否者方法名称会变得很长很难看,还有在涉及关联查询的时候手写SQL更方便。

从查询方法名称中派生Query对象

public interface UserRepository extends JpaRepository<User, Long> {
    // 创建query对象操作JPA criteria api,该对象对应如下的JPQL查询语句
    // select from User u where u.emailAddress = ?1 and u.lastname = ?2
    List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

JPA使用“关键字”机制分割方法名称,建立多条件查询对象Query,该对象对应一条JQPL查询语句片段,关键字用于组合各种查询条件,JPA中关键字一共有二十多个,以下为常用的:

KeywordSampleJPQL snippet
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is , EqualsfindByFirstname , findByFirstnameEquals… where x.firstname = ?1
LessThanfindByAgeLessThan… where x.age < ?1
GrateThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
LikefindByFirstnameLike… where x.firstname like ?1

方法名称中可以使用关键字top或者first来过滤出查询结果的前n条记录:

List<User> findFirst10ByLastname(String lastname);

@Query 的使用方法

Spring Data JPA 中可以在查询方法上添加@Query注解来定义一条 JPQL 查询语句:

public interface UserRepository extends JpaRepository<User, Long> {
    // 注意模糊查询的使用
    @Query("select from User u where u.firstname like %?1")
    List<User> findByFirstnameEndsWith(String firstname);
}

@Query 注解允许创建原生的SQL查询语句而不是JPQL,使用原生的SQL查询语句的好处是可以在SQL图形化界面中调试好了之后再copy到代码中,防止出错。返回的结果集被自动映射封装为方法返回值声明类型的对象中。

public interface UserRepository extends JpaRepository<User, Long> {
    // 使用 nativeQuery = true 开启原生SQL语句查询
    @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
    User findByEmailAddress(String emailAddress);
    
    @Modifying
    @Query("update User u set u.firstname = ?1 where u.lastname = ?2")
    int setFixedFirstnameFor(String firstname, String lastname);
}

@Modifying 注解

在@Query注解中编写 JPQL 实现 UPDATE 和 DELETE 的时候必须在方法上加上@Modifying 注解,通知 Spring Data 这是删除或者更新操作,同时在Service层需要添加事务的支持@Transactional,注意JPQL是不支持INSERT操作的,代码如上。

四. Specifications

JPA 2 规范中引入了一个条件查询的api,这个 criteria api 可以使用编程的方式手动设置查询的条件(where子句)。在 Spring Data JPA 中使用 criteria api 只需要继承接口JpaSpecificationExecutor,该接口中的查询方法接收一个Specification 类型的对象,这个对象中封装了where子句中的查询条件:

public interface CustomerRepository extends JpaRepository<Customer, Long>, JpaSpecificationExecutor {
    // 继承JpaSpecificationExecutor接口,实现条件查询,这个方法特别使用于页面多参数的
    // 条件查询,需要在运行中动态构建SQL语句,类似于mybatis中动态SQL语句
}

构建动态查询条件对象:

/**
 * 动态条件构建
 * @param searchMap
 * @return
 */
private Specification<Problem> createSpecification(Map searchMap) {

    return new Specification<Problem>() {

        @Nullable
        @Override
        public Predicate toPredicate(Root<Problem> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            List<Predicate> predicateList = new ArrayList<Predicate>();
            // ID
            if (searchMap.get("id")!=null && !"".equals(searchMap.get("id"))) {
                predicateList.add(cb.like(root.get("id").as(String.class), "%"+(String)searchMap.get("id")+"%"));
            }
            // 标题
            if (searchMap.get("title")!=null && !"".equals(searchMap.get("title"))) {
                predicateList.add(cb.like(root.get("title").as(String.class), "%"+(String)searchMap.get("title")+"%"));
            }
            // 内容
            if (searchMap.get("content")!=null && !"".equals(searchMap.get("content"))) {
                predicateList.add(cb.like(root.get("content").as(String.class), "%"+(String)searchMap.get("content")+"%"));
            }
            // 用户ID
            if (searchMap.get("userid")!=null && !"".equals(searchMap.get("userid"))) {
                predicateList.add(cb.like(root.get("userid").as(String.class), "%"+(String)searchMap.get("userid")+"%"));
            }
            // 昵称
            if (searchMap.get("nickname")!=null && !"".equals(searchMap.get("nickname"))) {
                predicateList.add(cb.like(root.get("nickname").as(String.class), "%"+(String)searchMap.get("nickname")+"%"));
            }
            // 是否解决
            if (searchMap.get("solve")!=null && !"".equals(searchMap.get("solve"))) {
                predicateList.add(cb.like(root.get("solve").as(String.class), "%"+(String)searchMap.get("solve")+"%"));
            }
            // 回复人昵称
            if (searchMap.get("replyname")!=null && !"".equals(searchMap.get("replyname"))) {
                predicateList.add(cb.like(root.get("replyname").as(String.class), "%"+(String)searchMap.get("replyname")+"%"));
            }

            return cb.and( predicateList.toArray(new Predicate[predicateList.size()]));
        }
    };
}

然后调用条件查询:

/**
 * 条件查询+分页
 *
 * */
public Page<Problem> pageQuery(Map searchMap, int page, int size){
    Specification<Problem> specification = createSpecification(searchMap);
    Pageable pageable = PageRequest.of(page - 1, size);

    return problemDao.findAll(specification, pageable);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值