对象关系映射, 创建实体类和表的关系映射关系, 实体类和表中字段的映射关系,咱们操做实体类底层是操做数据表, 进而自动的拼接出SQL语句java
Jpa(Java Persistence Api) java持久层的api,是SUN公司提出的一套规范,也就是说,是由接口和抽象类组册,jpa自己不干活,真正干活的是hibernate,toplink等等对规范具体实现的框架, 有了这套规范以后,咱们是面向这套规范编程的,也就是说,当咱们想把项目中的Hibernate替换成toplink,咱们的java代码是不须要修改的,而仅仅修改配置文件,切换jar包mysql
咱们经过注解完成两件事:spring
// 声明此类是实体类 @Entity // 声明此类是实体类 @Table(name = "表名")
主键策略 | 做用 |
---|---|
IDENTITY | 自增(要求底层的数据库支持自增如mysql, Oracle就不支持) |
SEQUENCE | 序列(要求底层的数据库支持序列, 如Oracle) |
TABLE | JPA的支援, JPA会帮咱们惟一另外一张表, 里面记载了本表的记录数 |
AUTO | 自适应,让程序根据运行的环境自动选择策略, 其实就是 TABLE策略 |
@Id @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "表中的字段名")
myJpa = Persistence.createEntityManagerFactory("myJpa")
myJpa.createEntityManager()
EntityTransaction transaction = entityManager.getTransaction(); transaction.begin();
transaction.commit();
entityManager.close();
注意点: 1. 若是不添加事务, 是不会持久化的 2. 获取实体管理类工厂的方法时耗时的,并且实体管理类工厂可重复使用,所以把他抽取出去, 类一加载就执行sql
经常使用方法数据库
public void persist(Object entity);
public <T> T getReference(Class<T> entityClass, Object primaryKey);
public <T> T find(Class<T> entityClass, Object primaryKey);
public void remove(Object entity);
find()和getReference()
的区别:
find当即执行,返回实体类对象,而和getReference返回的是实体类的代理对象, 懒加载,当我使用对象的属性时才执行查询语句编程
#### jpqlapi
jpql: Java Persistence Query Language 根据实体类和属性进行查询app
其中jpql没有select * 这种写法,而是直接省去了, 由于是面向对象的查询语言, 因此它的查询语句向下面这样写
java 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();
SpringDataJpa是Spring对jpa的整合,封装,基于SpringDataJpa的规范咱们能够更方便的进行持久层的操做, SpringDataJpa底层干活的是Hibernate框架fetch
被spring整合后,相关的配置可经过spring.jpa....设置
public interface CustomerRepository extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {}
其中:
简单的CRUD
当咱们使用自定义的Repository点一下的时,基本的CRUD基本上打眼一看就知道怎么使用了, 下面说一下,比较类似的方法
方法名 | 做用 |
---|---|
getOne() | 根据Id获取单个实体类,底层使用的是Jpa的getReference() 懒加载 |
findOne() | 一样是根据Id获取单个实体,当即加载 |
save() | 更新 若id存在 / 新增 若id为空 |
使用注解@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);
其中:
例:
分页查询
// 当前查询第几页, 每一页查询的条数 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; });
注意点:
findAll(Specification spec,Pageable pageable)
,和不带条件的分页findAll(Pageable pageable)
gt lt le like
咱们须要分步, 1,获得path对象,2.根据path对象指定比较的参数类型在进行下一步比较,由于可能比较的是字符串, 也多是数字数据库表之间不免会出现彼此的约束, 如商品分类表和商品表之间,就是典型的一对多的关系,同一个分类下有多种不一样的商品,下面就是jpa如何经过注解控制一对多的关系
@OneToMany
@ManyToOne
@JoinColumn(name = "customer_id",referencedColumnName = "id")
注解, 做用是指明外键列名,以及引用的主键列名关于主键的维护:
通常咱们会选在让多的一方维护外键,不是由于一的一方不能维护,在一对多的关系中,双方均可以进行主键的维护,而且咱们把这种关系叫作双向管理,可是双方都维护主键,就会使得多出一条update语句,产生资源的浪费,缘由以下:
所谓维护主键,就好比说咱们经过jpa的save方法插入主表中的实体1和从表中的实体2,若是咱们没有进行双方之间的关联,两条数据会被添加进数据库,可是外键部分却为null; 所以咱们能够把维护主键看做是负责更新外键字段,这时若是双方都维护的话,就是出现两次update外键字段的sql
总结: 如下是OneToMany的最终方案
one:
mappedBy经过他指明,本身放弃维护外键,而参考Many端对外键的维护的实现 @OneToMany(mappedBy= "customer") // EAGER当即加载 LAZY: 延迟加载 private Set<LinkMan> linkManSet = new HashSet<>();
Many
targetEntity: 指明One的一方的字节码 name: 本表中的外键的列名, 由于在多的一方维护的外键 referencedColumnName: 外键引用的主键的列名 @ManyToOne(targetEntity: = Customer.class) @JoinColumn(name = "customer_id",referencedColumnName = "id") private Customer customer;
cascade
级联操做再One的一端进行配置
类型 | 做用 |
---|---|
ALL | 级联全部(推荐) |
PERSIST | 保存 |
MERGE | 更新 |
REMOVE | 删除 |
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
级联保存: 同时存在One和Many两个对象,咱们在保存One的同时级联保存Many方的对象
级联删除:
状况1: One的一方在维护主键, 这是的级联删除就会分两步走 ,首先删除外键,而后删除One的一方,同时删除One级联的去所有Many方
状况2: One的一方再也不维护主键,不能级联删除
例子: User和Role 多对多的关系
在User端,主动放弃对外键的维护权
@ManyToMany(mappedBy = "users",cascade = CascadeType.ALL) public Set<Role> roles = new HashSet<>();
在Role端,维护着外键, 负责对中间表上外键的更新的操做
/** * 配置多对多 * 1. 声明关系的配置 * 2. 配置中间表(包含两个外键) * targetEntity: 对方的 实体类字节码 * */ @ManyToMany(targetEntity =User.class) @JoinTable( name = "user_role",// name 中间表名称 joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}, // 当前对象,在中间表中的外键名, 以及参照的本表的哪一个主键名 inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")} // 对方对象在中间表的外键 ) public Set<User> users = new HashSet<>();
所谓对象导航查询,就是首先使用jpa为咱们提供的Repository查询获得结果对象,再经过该对象,使用该对象的get方法,进而查询出它关联的对象的操做
在一对多的关系中, get的属性是 Set集合, 而在多对一的关系中,get的属性是它维护的那个One端的引用
总结:
模式 | 做用 |
---|---|
一查多 | 默认延迟加载,由于有可能一会儿级联查询出成百上千的数据,可是咱们却不用 |
多查一 | 默认当即查询,多查一条数据 |