對象關係映射, 創建實體類和表的關係映射關係, 實體類和表中字段的映射關係,咱們操做實體類底層是操做數據表, 進而自動的拼接出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端的引用
總結:
模式 | 做用 |
---|---|
一查多 | 默認延遲加載,由於有可能一會兒級聯查詢出成百上千的數據,可是咱們卻不用 |
多查一 | 默認當即查詢,多查一條數據 |