Jpa 笔记 (一)

简介: Jpa 笔记 (一)

ORM 思想#


对象关系映射, 建立实体类和表的关系映射关系, 实体类和表中字段的映射关系,我们操作实体类底层是操作数据表, 进而自动的拼接出SQL语句


Jpa规范#


Jpa(Java Persistence Api) java持久层的api,是SUN公司提出的一套规范,也就是说,是由接口和抽象类组成,jpa本身不干活,真正干活的是hibernate,toplink等等对规范具体实现的框架, 有了这套规范之后,我们是面向这套规范编程的,也就是说,当我们想把项目中的Hibernate替换成toplink,我们的java代码是不需要修改的,而仅仅修改配置文件,切换jar包


上手: jpa规范#


常见的注解#


我们通过注解完成两件事:

  1. 实体类和数据表之间的关系的映射
  2. 实例类属性和数据表字段之前的映射


  • 添加在类头上的注解


// 声明此类是实体类
@Entity
// 声明此类是实体类
@Table(name = "表名")


  • 标记主键


主键策略 作用
IDENTITY 自增(要求底层的数据库支持自增如mysql, Oracle就不支持)
SEQUENCE 序列(要求底层的数据库支持序列, 如Oracle)
TABLE JPA的支援, JPA会帮我们生成另一张表, 里面记载了本表的记录数
AUTO 自适应,让程序根据运行的环境自动选择策略, 我的程序选择了 TABLE策略


@Id
@GeneratedValue(strategy = GenerationType.AUTO)


  • 实体类属性和表中的字段的映射


@Column(name = "表中的字段名")


进行CRUD的开发步骤:#


  • 加载配置文件, 得到实体管理类工厂


myJpa = Persistence.createEntityManagerFactory("myJpa")


  • 通过实体管理类工厂获取实体管理器


myJpa.createEntityManager()


  • 获取事务对象, 开启事务


EntityTransaction transaction = entityManager.getTransaction();
 transaction.begin();


  • CRUD


  • 提交事务


transaction.commit();


  • 释放资源


entityManager.close();


注意点: 1. 如果不添加事务, 是不会持久化的 2. 获取实体管理类工厂的方法是耗时的,而且实体管理类工厂可重复使用,因此把他抽取出去, 类一加载就执行


常用方法

  • 添加public void persist(Object entity);
  • 根据主键Id查找public <T> T getReference(Class<T> entityClass, Object primaryKey);
  • 根据主键Id查找public <T> T find(Class<T> entityClass, Object primaryKey);
  • 删除public void remove(Object entity);

find()和getReference()的区别:

find立即执行,返回实体类对象,而和getReference返回的是实体类的代理对象, 懒加载,当我使用对象的属性时才执行查询语句


jpql#


jpql: Java Persistence Query Language 根据实体类和属性进行查询

其中jpql没有select * 这种写法,而是直接省去了, 因为是面向对象的查询语言, 所以它的查询语句向下面这样写


from 带包名的类的全路径/直接写类名


  • 排序


from  类名 order by id desc/asc


  • 统计数量


select count(id) from 类名


  • 带条件的查询


EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// 查询全部
String jpql = "from 类名 where name like ?";
//  String jpql = "from  类名";   可省略包名
Query query = entityManager.createQuery(jpql);
// 参数1: 占位符的位置
// 参数2: 参数的值
query.setParameter(1,"张%");
query.getResultList().forEach(System.out::println);
transaction.commit();
entityManager.close();


  • 分页查询


EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// 查询全部
String jpql = "from 类名";
Query query = entityManager.createQuery(jpql);
// 对分页的参数赋值
// 起始索引
query.setFirstResult(0);
// 分页参数, 每次查询两条
query.setMaxResults(2);
// 查询,斌封装结果集
List resultList = query.getResultList();
resultList.forEach(System.out::println);
transaction.commit();
entityManager.close();


Spring Data Jpa#


SpringDataJpa是Spring对jpa的整合,封装,基于SpringDataJpa的规范我们可以更方便的进行持久层的操作, SpringDataJpa底层干活的是Hibernate框架


开发步骤#


被spring整合后,相关的配置可通过spring.jpa....设置

  1. 做好实体类和数据表之间的关系的映射
  2. 面向接口编程,我们只要自己新建一个接口,并且继承JpaRepository和JpaSpecificationExecutor这两个接口就可以使用它的方法,而不需要关心实现类如何,就像下面:具体的实现类会通过JDK的动态代理为我们自动生成,


public interface CustomerRepository extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {}


其中:

  • JpaRepository(继承自CRUDRepository) 封装了基本的CRUD
  • JpaSpecificationExecutor 封装了复杂查询

简单的CRUD

当我们使用自定义的Repository点一下的时,基本的CRUD基本上打眼一看就知道怎么使用了, 下面说一下,比较相似的方法


方法名 作用
getOne() 根据Id获取单个实体类,底层使用的是Jpa的getReference() 懒加载
findOne() 同样是根据Id获取单个实体,立即加载
save() 更新 若id存在 / 新增 若id为空


支持 自定义sql / jpql / 方法命名规则 查询#


使用注解@Query


例:


@Query(value = "select * from  Customer where name = ?", nativeQuery = true)
public Customer findByNameAndSQL(String name);
// 查询全部
@Query(value = "select * from  Customer", nativeQuery = true)
public List<Customer> findAllBySQL();


其中的@Query的第三个参数默认是false 表示不是sql查询,而是jpql查询



// jpql 查询全部
@Query(value = "from  Customer where name =?1", nativeQuery = false)
public Customer findAllByNameAndJpql();


SpringDataJpa对jpql再次进行了封装,支持方法命名规则查询:


查询方式 命名规则
根据某个字段查询 find实体类名By字段名
模糊查询 find实体类名By字段名Like , 注意传参时不要忘了添加%
多条件并列查询 find实体类名By字段名And字段名 ,使用and关键字隔开
多条件或查询 find实体类名By字段名Or字段名 ,使用Or关键字隔开


复杂查询#


Optional<T> findOne(@Nullable Specification<T> spec);
List<T> findAll(@Nullable Specification<T> spec);
//Page 是 SpringDataJpa提供的
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
// 查询条件spec
// 排序条件 sort
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
// 按照条件统计
long count(@Nullable Specification<T> spec);


他们的公共入参都有Specification 这是个接口,我们需要自己实现, 重写它的抽象方法


Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);


其中:

  • root: 是我们查询的根对象(查询的任何属性都能从根对象中获取)
  • CriteriaQuery: 顶层的查询对象
  • CriteriaBuilder: 查询的构造器, 封装了很多查询条件


例:

分页查询


// 当前查询第几页, 每一页查询的条数
Pageable pageable =  PageRequest.of(0,2);
Page<Customer> page =  customerRepository.findAll((root, query, criteriaBuilder)->{
    return null;
}, pageable);
System.out.println("page.getTotalElements():  "+        page.getTotalElements()); // 总条数
System.out.println("page.getTotalPages():  "+        page.getTotalPages()); // 总页数
page.getContent().forEach(System.out::println);  // 当前页结果


排序


/**
 *  参数1 ; 正序 / 倒叙
 *  参数2 : 属性名
 */
Sort orders = new Sort(Sort.Direction.DESC,"id");
List<Customer> list=  customerRepository.findAll((root,query,criteriaBuilder)->{
    Path<Object> name = root.get("name");
    Predicate like = criteriaBuilder.like(name.as(String.class), "武%");
    return like;
},orders);


模糊查询


List<Customer> list=  customerRepository.findAll((root,query,criteriaBuilder)->{
    Path<Object> name = root.get("name");
    Predicate like = criteriaBuilder.like(name.as(String.class), "武%");
    return like;
});


多条件查询


/**
 *  root 获取属性
 *  criteriaBuilder: 构造查询条件
 */
Optional<Customer> customer=  customerRepository.findOne((root,query,criteriaBuilder)->{
      Path<Object> name = root.get("name");
      Path<Object> industry = root.get("industry");
      Predicate namepre = criteriaBuilder.equal(name, "张三");
      Predicate indpre = criteriaBuilder.equal(industry, "学生");
    /* 组合条件
        1. 满足条件1和条件2
        2. 满足条件1或条件2
    * */
   Predicate andpre = criteriaBuilder.and(namepre, indpre);
  //  Predicate or = criteriaBuilder.and(namepre, indpre);
    //  以 或的条件查询
    return andpre;
});


// 多条键查询尽量写成下面的样子,保证每个条件都是有效的
 public void search(Label label) {
        labelRepository.findAll((root, query, caiteria) -> {
            ArrayList<Predicate> predicateList = new ArrayList<>();
            if (label.getLabelName()!=null){
                Path<Object> labelname = root.get("labelname");
                Predicate like = caiteria.like(labelname.as(String.class), "%" + label.getLabelName() + "%");
                predicateList.add(like);
            }
            if (label.getRecommend()!=null){
                Path<Object> rec = root.get("recommend");
                Predicate like = caiteria.like(rec.as(String.class), "%" + label.getRecommend() + "%");
                predicateList.add(like);
            }
            Predicate[] predicates = new Predicate[predicateList.size()];
            predicateList.toArray(predicates);
            // 参数位置支持可变参数, 但是我们要尽量传递进去有效的条件
            return caiteria.and(predicates);
        });


注意点:

  • 分页两种: 带条件的分页findAll(Specification spec,Pageable pageable) 和不带条件的分页findAll(Pageable pageable)
  • 此外: 对于criteriaBuilder的equals方法,可以直接使用path对象,但是对于 gt lt le like我们需要分步, 1. 得到path对象,2. 根据path对象指定比较的参数类型在进行下一步比较,因为可能比较的是字符串, 也可能是数字



相关文章
|
移动开发 C++ Perl
学习C++笔记424
C++ Web 编程
100 0
|
Shell C++ Python
学习C++笔记405
C++ Web 编程
92 0
|
Unix Linux C++
学习C++笔记388
C++ 信号处理
81 0
|
编译器 C++
学习C++笔记359
C++ 命名空间
79 0
|
存储 程序员 C++
学习C++笔记348
C++ 动态内存
101 0
|
iOS开发 C++
学习C++笔记336
C++ 文件和流
84 0
|
C++
学习C++笔记335
C++ 文件和流
83 0
|
C++
学习C++笔记310
C++ 多态
102 0
|
C++
学习C++笔记292
C++ 继承
97 0
|
前端开发 C++
学习C++笔记264
C++ 数据结构
99 0