# 03.3-Data Stores and Rersistence Lesson3:Java Persistence API(JPA)
###### tags: `Udacity`
[ToC]
# 01 Introduction to Java Persistence API
Introduction to the Java Persistence API
{%youtube -hAHyi1u1AE%}
> You are here

**Lesson Outline**
* Persistence Context
* Learn the concept
* Entity Manager
* Use Entity Manager to change to stated of our entities in the persistence context
* Queries and JPQL
* Learn how to write object model queries using JPA's query language
* Projections
* Projecting data into different types of objects
* Repository Pattern & Spring Data JPA
* Simplify many of our common querying tasks.
* Transactions and Flushing
* How to define transaction boundaries to maintain integrity(正直) of our data.
**Definitions**
* Java Persistence API (JPA): A specification describing how to manage relational data
* Hibernate: An implementation of the JPA Specification
**Note about JPA rebranding(品牌重塑)**
JPA changed names in 2018 when Java EE was rebranded to Jakarta EE in an effort to separate the Enterprise Edition development from ongoing trademark(商標) issues surrounding the term Java in their framework name. Java EE, or Jakarta EE now, fills a similar role to Spring in that it provides a large number of tools to enable enterprise-scale projects. One of those tools is JPA, now Jakarta Persistence API (but, conveniently, still the same letters). Just something to keep in mind when searching for more info.
# 02 Persistence Context
## Udacity
Persistence Context
{%youtube DAd8RjeM5eY%}
**Definitions**
* Persistence Context: Describes the relationship between all the Entity instances in our program and their representations in the underlying database.
* Instance: A specific copy of an Entity in program memory.
**Persistence Context Entity States**
**Transient(temporary)**: not associated with the persistence **context**. Often has not yet had an ID assigned.
**Managed**: persistent. Managed by the current persistence context. Changes to the entity will be reflected in the backing database.
**Detached(separated)**: previously managed. Occurs to all managed entities when persistence context ends.
**Removed**: scheduled to be removed from the database. Java object still exists and has ID.

Additional Resources
[Hibernate Documentation on Persistence Context](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc)
## ShannonNote
Persistence Context: Describes the relationship between all the Entity instances in our program and their representations in the underlying database.
四種狀態 [來源](https://docs.jboss.org/hibernate/orm/3.5/reference/zh-CN/html/objectstate.html)
- 瞬时(Transient) — 由 new 操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识(identifier)。 如果瞬时(Transient)对象在程序中没有被引用,它会被垃圾回收器(garbage collector)销毁。 使用 Hibernate Session可以将其变为持久(Persistent)状态。(Hibernate会自动执行必要的SQL语句)
- 持久(Persistent) — 持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行UPDATE。将对象从持久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行 DELETE 语句。
- 脱管(Detached) — 与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的 Session 上, 会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)。 这个功能使得一种编程模型,即中间会给用户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可能。我们称之为应用程序事务,即从用户观点看是一个操作单元(unit of work)。
* Removed: scheduled to be removed from the database, java object still exists and has ID.
# 03 Entity Manager
## Udacity
{%youtube zJ2vhVApiY4%}
**Changing Persistence States**
**Persist**: Takes an Entity not yet managed. The Entity becomes managed and will be saved to the database.
**Find**: Looks up an id in the database and returns a managed Entity.
**Merge**: Updates an Entity that is in the detached state. Returns an instance of that Entity that is now managed. If Entity was not found in the database to update, persists Entity as a new row.
**Remove**: Detaches an entity and deletes it from the database.
> Example Code
```java
@PersistenceContext
EntityManager entityManager;
public void persistExample(Person p) {
entityManager.persist(p); //write p to the database
p.setFavoriteComposer("Johann Strauss II"); //will update database
}
public void findExample(Long id) {
Person p = entityManager.find(Person.class, id); //retrieve an instance by its key
p.setFavoriteComposer("Sir Malcolm Arnold"); // will update database
}
public void getReferenceExample(Long personId, Long outfitId) {
Person p = entityManager.find(Person.class, personId);
Outfit outfitReference = entityManager.getReference(Outfit.class, outfitId);
p.getOutfits().add(outfitReference);
}
public void mergeExample(Person detachedPerson){
detachedPerson.setFavoriteComposer("Rimsky Korsakov");
Person managedPerson = entityManager.merge(detachedPerson);
detachedPerson.setFavoriteComposer("Antonio Salieri"); //will have no effect on database
managedPerson.setFavoriteComposer("C.P.E. Bach"); //will overwrite Korsakov
}
public void deleteExample(Long id) {
Person p = entityManager.find(Person.class, id); //retrieve an instance by its key
entityManager.remove(p); //will delete row from database
}
```
**Note about Hibernate Session**
Hibernate exposes some methods beyond those specified in the JPA API. Hibernate-specific methods can be accessed through an object called Session, which is the Hibernate implementation of EntityManager. You can acquire a Session object by using the method `entityManager.unwrap(Session.class)`. You can use Session to access some Hibernate’s own implementation methods. [See the Hibernate documentation on Persistence Context](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc) for more details about Session as well as the above four methods.
## ShannonNote
**What can EntityManager do ? **
- creating and deleting persistent entites
- find entities or execute other quesies.
**How to use EntityManager?**
- inject an entity manager into your class.
```java
@Transactional
public class EntityManagerDemo{
@PersistenceContext
EntityManager entityManager;
/*functions that persist, remove, find, and merge*/
}
```
**Persist()**
- take new entites which is not managed, change its states to manage and save into the database.
> the example of persist
```java
public void persistExample(Object obj){
// write new entites to the database, change the states
entityManager.persist(obj);
// when the entites was managed, you can update the info from obj, the database will follow the changes
obj.setAttribute("new value")
}
```
**find**
- find the entity you want by id and return the entity
- using `entity.setAttribute` to edit the info you want
> the example of EditFuntion()
```java
public void EditEntity(Long id){
//providing class and id, and then entitymanager will find the entites which was created by Object.class, and then use id to find the entity.
// * if entitymanger cannot find the entity which id is what you provide, it will return null.
Object obj = entityManager.find(Object.class, id);
// set the info to the obj
obj.setAttribute("new value");
}
```
> Conclustion between persist and edit : persist have to persist but edit do not need. Because the entity you get from entitymanager.Find() have already in the managed state.
**Reference : Copy an entity**
- if you need a copy of an entity in order to associate it with another object. But do not want to change its value
```java
public void getReferenceExample(Long customerId, Long ProductId){
//if customer wants to add the Product
Customer cus = entityManager.find(Customer.class, customerId);
// but you don't want to change the Product info, just want to reference that the customer order the product
Product product = entityManger.getReference(Product.class, ProductId);
// add the product into the customer
cus.getProducts().add(product);
}
```
**merge**
- in order to update an entity that is in the detached state
- if you use `entityManager.merge(object)` after setnewAttribute , it will renew the entity's info in the database, but if you just change the info after merge, the new info will not reflect in the database.
```java
public void EditInfoUseMerge(Customer DetechedCustomer, String address, String phone){
//set new info before merging
DetechedCustomer.setAddress(address);
//new address write into the database, and return managed state customer
Customer managedCustomer = entityManager.merge(DetechedCustomer);
//use managed state customer to change the phone info will reflect into the database
//new phone number write into the database now.
managedCustomer.setPhone(phone);
//if you use the detechedCustomer with no merging, it is not effect on database
DetechedCustomer.setAddress("Taiwan");
}
```
==notice==
> if you use merge to find the entity, but the entity did not exist yet. Hibernet will create a new entity for you and return the managed copy. But it is not recommended to use merge for this purpose. Because Every merge call has to look up the existing object first. So for this purpose, you should use `persist()` for new entities and merge for detached entities.
**Remove**
- use `entityManager.remove` will detach an entity from the persistence context and delete it from the database
- `remove` method only work on the entity which is managed by the persistence context, so if you want to delete a transient state or detached state entity, you will have to find it first, change state to managed. After that, you could use remove method.
```java
public void delete(Long Customerid, Long ProductId) {
//retrieve an instance by its key
Customer cus = entityManager.find(Customer.class, Customerid);
//will delete row from database
entityManager.remove(cus.getProducts(ProductId));
}
```
# 04 Lazy Loading
## Udcaity
{%youtube kk4kIAH1XdM%}
Setting a fetch strategy can prevent your Entities from loading associated values until those values are referenced.
**FetchType.EAGER**
Always retrieve the associated values as part of the Entity retrieval. This means the initial query for the entity retrieves this data.
**FetchType.LAZY**
Wait to retrieve associated values until they are referenced. Lazy-loaded attributes are Hibernate proxy objects whose specific values are retrieved from the database only if they’re accessed. The initial query for the entity will NOT retrieve this data.
Example Usage
```java
@Entity
public class Person {
@Id
@GeneratedValue
Long id;
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
List<Outfit> outfits;
private String name;
private int age;
private String favoriteComposer;
/* rest of class */
}
```
**Default Values**
An easy way to remember this is that both associations mapping to Many objects default to Lazy, because it’s more costly to retrieve lots of objects from the database. Associations mapping to One object default to Eager, because there’s usually less information.
**FetchType.LAZY:**
- @OneToMany
- @ManyToMany
**FetchType.EAGER:**
- @ManyToOne
- @OneToOne
Additional Resources
[Hibernate documentation on Fetching](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#fetching)
## ShannonNote
> Question: 如何處理references entity? 在存取方面會增加速度?
- FetchType.LAZY
- @OneToMany 跟 @ManyToMany 的default value 我們可以這樣寫`@OneToMany(fetch = FetchType.LAZY)
- 如果加上這個屬性,那只有在真正需要資料的時候才會去資料庫索取
- 她的好處在,當我們要載入Customer這個物件的時,如果Customer裡面的`List<Order>`加上FetchType.LAZY,並不會一次把所有的reference 的 order都載入,可以增加讀取時間
> Example for Customer.java
```java
public class Customer{
@Id
@GenerateValue
private Long id;
private
@Column(name='order')
//mappedby代表會連接到Order.class裡面的Customer 類的Attribute名稱 像這樣 private Customer hay_order
@OneToMany(mappedby="hay_order" ,fetch=FetchType.LAZY)
private List<Order> orders;
//getter and setter
}
```
> Example for EntityManager which will be writen at Repository
```java=
public List<Order> findOrderById(Long CustomerId){
//這時候Orders的資料還沒載入
Customer cus = entitymanager.find(Customer.class, CustomerId);
//這時候需要資料的時候才會去索取
return cus.getOrders();
}
```
- FetchType.EAGER
- 她跟LAZY相反,default為@OneToOne或是@ManyToOne在用的,畢竟不需要太多時間去搜尋資料
- 因此當物件載入的時候就可以預先把資料都載好
# 05 Cascading
## Udacity
**Cascading Persistence Operations**
{%youtube QC8tTxRyAKo%}
CascadeType allows us to modify Entity associations so that persistence operations on one Entity will cascade to other Entities associated with it.
**Example Usage**
```java
@Entity
public class Person {
@Id
@GeneratedValue
Long id;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
List<Outfit> outfits;
private String name;
private int age;
private String favoriteComposer;
/* rest of class */
```
Valid CascadeTypes correspond to the different persistence operations, such as Persist, Merge, and Remove. [See the Hibernate cascade documentation](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc-cascade) for more details.
## ShannonNote
參考:
- https://www.itread01.com/content/1546196593.html
- https://www.itread01.com/content/1550238489.html
先介紹一下Cascade,指的是當主控方執行操作時,關聯物件(被動方)是否同步執行同一操作。
介紹幾個CascadeTypes
- CascadeTye.ALL: 就是以下標註都含
- CascadeTye.PERSIST
- 父Entity新增的時候 子entity也會新增一個table,但是兩個並沒有refernece起來
- 只有parent新增時,才會Cascade child 新增
- CascadeTye.MERGE
- 會產生父entity跟子entity table,也會reference起來
- when parent entity change or update, the child entity change too.
- CascadeTye.REMOVE
- when hibernate find the parent entity, it will also reflesh the database in order to get the newest child entity info.
> Most of the time we used Merge
# 06 JPA Exercise 1
**JPA Exercise 1**
For this exercise, you should create a class that can add new Delivery objects to the database. It should support the following interface:
```java
void persist(Delivery delivery);
Delivery find(Long id);
Delivery merge(Delivery delivery);
void delete(Long id);
```
Modify Delivery so that if it is removed, it will also remove any Plants associated with it at the same time.
Modify Plant so that it will only query for Delivery objects when they are referenced, not every time the Plant is retrieved.
> JPA Exercise 1 Task List
[ ] Create a new class called DeliveryRepository
[ ] Create methods to persist, find, merge, and delete Delivery objects.
[ ] Modify Delivery so that it will delete any associated Plants when remove.
[ ] Modify Plant so that it won't query for Delivery objects until they're referenced.
# 07 Solution: JPA Exercise 1
We’re really making progress! Your service can finally schedule new deliveries!
**DeliveryRepository.java**
```java
@Repository
@Transactional
public class DeliveryRepository {
@PersistenceContext
EntityManager entityManager;
public void persist(Delivery delivery) {
entityManager.persist(delivery);
}
public Delivery find(Long id) {
return entityManager.find(Delivery.class, id);
}
public Delivery merge(Delivery delivery){
return entityManager.merge(delivery);
}
public void delete(Long id) {
Delivery delivery = entityManager.find(Delivery.class, id);
entityManager.remove(delivery);
}
}
```
**Plant.java**
```java
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Plant {
@Id
@GeneratedValue
private Long id;
@JsonView(Views.Public.class)
@Nationalized
private String name;
@JsonView(Views.Public.class)
@Column(precision=12, scale=4)
private BigDecimal price;
//don't retrieve delivery if we don't need it
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
```
**Delivery.java**
```java
@Entity
public class Delivery {
@Id
@GeneratedValue
private Long id;
@Nationalized
private String name;
@Column(name = "address_full", length = 500)
private String address;
private LocalDateTime deliveryTime;
@Type(type = "yes_no")
private Boolean completed;
// added CascadeType.REMOVE to automatically clear any associated plants when removed
@OneToMany(fetch = FetchType.LAZY, mappedBy = "delivery", cascade = CascadeType.REMOVE)
private List<Plant> plants;
/* getters and setters */
}
```
# 08 Queries
## Udacity
{%youtube jMjsA872Kx8%}
JPQL allows us to write queries for the EntityManager that return objects directly. Their syntax is very similar to SQL, but we reference Entities instead of tables.
**SQL**
```sql
SELECT *
FROM person p /* Table name */
WHERE p.favoritecomposer LIKE '%Sibelius%' /* Column */
```
**JPQL**
```sql
SELECT p
FROM person p /* Entity */
WHERE p.favoritecomposer LIKE '%Sibelius%' /* Attribute */
```
**Creating a JPQL Query**
To create a query, inject an entityManager into your class and then use the `createQuery` method. This method returns different types of Query objects depending on your parameters. TypedQuery is recommended for clarity.
```java
private static final String FIND_PERSON_BY_COMPOSER =
"select p from Person p " +
"where p.favoriteComposer like :favoriteComposer";
public Person findPersonByFavoriteComposer(String favoriteComposer){
TypedQuery<Person> query = entityManager.createQuery(FIND_PERSON_BY_COMPOSER, Person.class);
query.setParameter("favoriteComposer", favoriteComposer);
return query.getSingleResult();
}
```
**Referencing Associated Entities**
In SQL, you will often join tables together to search for results by related Entities. In JPQL, you can reference the value of associated Entities by accessing them directly as Entity attributes in the query.
```java
private static final String FIND_HUMANOID_BY_OUTFIT =
"select h from Humanoid h " +
"where :outfit member of h.outfits";
List<Humanoid> findHumanoidByOutfit(Outfit o){
TypedQuery<Humanoid> query = entityManager.createQuery(FIND_HUMANOID_BY_OUTFIT, Humanoid.class);
query.setParameter("outfit", o);
return query.getResultList();
}
```
Additional Resources
[Hibernate Documentation on queries](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#hql)
## ShannonNote
> 目前的狀況來說我們可以透過entitymanager進行簡單的CRUD工作,但是如果想要透過特殊的properties或是associations they hava該怎麼做?
- 這時候就需要Query囉! 通常如果是特別寫JPA的Query我們會稱她為JPQL,她跟SQL不同的地方在,SQL是透過table進行query但是JPQL是透過entity進行query然後再轉換成SQL給我們
**用法**
- 首先我們要先寫String query
- 然後透過entityManager.createQuery("write your query here", the object.class you want to return)來執行JPQL
- 然後使用`TypedQuery<Object>`來接收entityManager傳回來的entity
- 最後透過TypedQuer.getSingleResult來回傳entity
> 範例
```java
private static final String FIND_PERSON_BY_COMPOSER =
"select p from Person p " +
"where p.favoriteComposer like :favoriteComposer";
public Person findPersonByFavoriteComposer(String favoriteComposer){
TypedQuery<Person> query = entityManager.createQuery(FIND_PERSON_BY_COMPOSER, Person.class);
query.setParameter("favoriteComposer", favoriteComposer);
return query.getSingleResult();
}
```
# 09 Named Queries
## Udacity
{%youtube 97KRHZtt8So%}
Named queries can help us organize our queries by class. They also allow us to compiler-check our queries for validity at build time. Any named queries that reference invalid entities will throw exceptions, helping ensure we don’t commit invalid query strings. To use them, declare them at the top of the Entity class to which they refer. Remember that their names are global across the whole persistence unit, so they should all have unique names.
```java
@NamedQueries({
@NamedQuery(
name = "Outfit.findByHat",
query = "select o from Outfit o where o.hat = :hat"),
@NamedQuery(
name = "Outfit.findBySock",
query = "select o from Outfit o where o.sock = :sock")
})
```
You can also use the criteria builder to construct queries dynamically using Java code. We can represent the following query with criteria builder as follows.
```sql
SELECT h FROM Humanoid h
WHERE :outfit MEMBER OF h.outfits
```
```java
List<Humanoid> findHumanoidByOutfitCriteria(Outfit o) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Humanoid> criteria = cb.createQuery(Humanoid.class);
Root<Humanoid> root = criteria.from(Humanoid.class);
criteria.select(root).where(cb.isMember(o, root.get("outfits")));
return entityManager.createQuery(criteria).getResultList();
}
```
**Note about Legacy CriteriaBuilder**
Watch out for a legacy Hibernate CriteriaBuilder. This version has been deprecated in favor of the jakarta.persistence.CriteriaBuilder, so make sure you use entityManager.getCriteriaBuilder, NOT session.getCriteriaBuilder.
**Additional Resources**
[Hibernate Documentation for JPQL](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#jpql-api)
[Hibernate Documentation for CriteriaBuilder](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria)
## ShannonNote
在這裡提供幾種使用query的方法
1. Constant String Query
她就是一整句話直接寫死在程式上面
```java
private static final String FIND_ORDER= "select cus from Customer as cus where :order member of cus.order"
```
2. 第二種是Fragment Query
就是把原本很長的一句話拆開寫
```java
private static final CUSTOMER = "select cus from Customer as cus";
private static final FIND_ORDER = "where :order member of cus.order";
```
* 如果要使用以上的query進行查詢entityManager要使用`createQuery(String s, Object.class)`
> 但是以上兩種有個缺點==就是只能等到application run起來才能發現錯誤==,但是要如何在程式run之前就發生錯誤呢?
- 我們可以使用`@NameQuerie`她有以下兩種寫法。但是她需要注意的是名稱不可以相同。
1. 一般寫法
```java
@NameQuery(
name="Customer.findorder",
query="select cus from Customer as cus where :order member of cus.order"
)
@NameQuery(
//notice: the name should be difference with above
name="Repository.findorder",
query="select rep from repository as rep where :order member of cus.order"
)
```
2. 巢狀寫法
```java
@NameQueries{
@NameQuery(
//notice: the name should be difference with above
name="Repository.findorder",
query="select rep from repository as rep where :order member of cus.order"
)
@NameQuery(
name="Customer.findorder",
query="select cus from Customer as cus where :order member of cus.order"
)
}
```
* 如果要使用以上的@NameQuery進行查詢entityManager要使用`createNamedQuery(String s, Object.class)`
> 你覺得這樣還不夠嚴格嗎? 如果想要更嚴格你可以直接用java method寫SQL語法,那就是CriteriaBuilder。
**原本SQL長這樣**
```sql
SELECT cus FROM CUSTOMER AS cus WHERE :order MEMBER OF cus.orders
```
**可是如果使用CriteriaBuilder會長這樣**
```java
List<Customer> findCustomerByOrderCriteria(Order o){
//建造builder物件
CriteriaBuilder criteriaBuilder = entityManager.CriteriaBuilder();
//create criteria for returning Customer objects
Criteria criteria = criteriaBuilder.createQuery(Customer.class);
//set the root as Customer.class
Root<Curstomer> root = criterica.from(Customer.class);
//build the query from the criteriaobject by chaining methods for each query clause.
//cirterial.select() : choose the entity base for our query
//.where() pass in criteriabuilder.isMember to search the condition in which that the order object is a member of the ourders attribute of our root.
criteria.select(root).where(criterica.isMember(o, root.get("orders")));
return entityManger.createQueriy(criteria).getResultList();
}
```
# 10 Projections
## Udacity
{%youtube 6fax_UKk5wE%}
Projections allow us to return non-Entity data from queries.
**Projecting into a Value Type**
```java
private static final String LIST_FAVORITE_COMPOSERS = "select distinct p.favoriteComposer from Person p";
List<String> listFavoriteComposers() {
TypedQuery<String> query = entityManager.createQuery(LIST_FAVORITE_COMPOSERS, String.class);
return query.getResultList();
}
```
**Projecting into a non-Entity Object**
```java
public class PersonComposerDTO {
private String name;
private String composer;
public PersonComposerDTO(String name, String composer) {
this.name = name;
this.composer = composer;
}
/* getters and setters */
}
```
```java
private static final String GET_PERSON_AND_COMPOSER =
"select new com.udacity.jdnd.course3.controller.PersonComposerDTO(p.name, p.favoriteComposer) " +
"from Person p " +
"where p.id = :id";
PersonComposerDTO getPersonComposer(Long id) {
TypedQuery<PersonComposerDTO> query = entityManager.createQuery(GET_PERSON_AND_COMPOSER, PersonComposerDTO.class);
query.setParameter("id", id);
return query.getSingleResult();
}
```
==Notice==
* 有幾個重要的點!!
* 使用JPQL的時候,`select obj from object obj`這裡的ojbect一定要使用java裡面跟class一模一樣的名稱! 因為他是透過java class去資料庫查詢並不是透過table
* 然後有時候使用DTO要看看inheritance的型態,例如customer有很多orders但是實際上資料庫customer並沒有order的欄位,因為他們是透過JOINED來inheritance
> 關於distict的語法可以參考補充01
## ShannonNote
有時候透過Jsonview標註接收一些訊息時,會接收到一些我們不需要的entity data,但我們可以會遇到一個entity包含很大的訊息量可是你需要用到的只有一點點,這時候該怎麼辦?
- 這時候就可以使用projection,projection還可以將兩個毫無關聯的enitity組合成一個non-entity single object
> 首先我們要一個non-entity object去裝我們從資料庫查到的東西成為物件 DTO(DATA TRANFER OBJECT)
```java
public class CustomerOrderDTO{
private String CustomerName;
private Order order;
public CustomerOrderDTO(String name, Order order){
this.CustomerName = name;
this.order = order;
}
}
```
> 接下來把我們從資料庫抓出來的資料塞進去DTO物件裡面
```java
private static final String LIST_CUSTOMER_ORDERS = "select new com.udacity.jdnd.course3.controller.CustomerOrderDTO(cus.name, cus.order)from Customer cus where cus.id=:id";
List<Order> listOrders(){
TypedQuery<Order> query = entityManager.createQuery(LIST_CUSTOMER_ORDERS);
query.setParameter("id", id);
return query.getResultList();
}
```
- 你可以看到 LIST_CUSTOMER_ORDERS 裡面的jpql語法,裡面創造了地址.DTO物件,然後把資料放進去
# 11 JAP Exercise2
**JPA Exercise 2**
Before we get to the assignment, let’s add basic CRUD support for Delivery items. Create the following DeliveryController and DeliveryService classes:
```java
@RestController
@RequestMapping("/delivery")
public class DeliveryController {
@Autowired
DeliveryService deliveryService;
@PostMapping
public Long scheduleDelivery(@RequestBody Delivery delivery) {
return deliveryService.save(delivery);
}
}
@Service
public class DeliveryService {
@Autowired
DeliveryRepository deliveryRepository;
public Long save(Delivery delivery) {
delivery.getPlants().forEach(plant -> plant.setDelivery(delivery));
deliveryRepository.persist(delivery);
return delivery.getId();
}
}
```
Let’s also set the ==cascade to `CascadeType.ALL`== for `List<Plant> plants` in Delivery.java, to make it easier for us to persist everything at once for testing.
> 幹這很重要,不然的話delivery新增可是plant那邊都不會有變動,也不會更新幹! 忘記改了,之前教學是寫delete,一定要記得改成ALL
You should now be able to use Postman to send a POST request to http://localhost:8080/delivery with the following JSON as a request body to create a Delivery object in your database:
```sql
{
"name": "Terry",
"address": "1234 Sesame Blvd",
"deliveryTime": "2020-03-07T18:07",
"plants": [
{"name": "Petunia",
"price": "3.50"},
{"name": "Tulip",
"price": "2.50"}
]
}
```
Now that you can add some data to test with, let’s get to the assignment.
For this assignment, we’ll write and use a named query. We’ll also use a CriteriaBuilder to populate a projection.
**Part 1 - Named Query**
Create a Named Query that returns a list of all Deliveries for a specified Name. Add a new method to your Delivery Repository that uses this query. It should accept a String name and return a List of Delivery Entities..
**Part 2 - CriteriaBuilder and Projections**
First, let’s make a projection class called RecipientAndPrice. It should contain a String name and a BigDecimal price.
Now let’s create a new method in DeliveryRepository that uses CriteriaBuilder to populate an instance of the RecipientAndPrice class. This method should take a Long deliveryId and return a RecipientAndPrice that contains the name of the delivery recipient and the sum of the prices of plants in their order.
There are a few ways to solve this one, and it may be a little tricky, so remember that you can consult the [Hibernate Documentation on constructing wrappers](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria-typedquery-wrapper) and the [Hibernate Documentation on Aggregate functions](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions), which are also supported by the CriteriaBuilder.
Feel free to create additional service and controller methods to test these capabilities.
# 12 Solution: JPA Exercise 2
Now we’re getting organized! Let’s look at some example solutions.
**Named Query in Delivery.Java**
```java
@NamedQuery(name = "Delivery.findByName",
query = "select d from Delivery d where d.name = :name")
@Entity
public class Delivery {
@Id
@GeneratedValue
private Long id;
// changed CascadeType to ALL
@OneToMany(fetch = FetchType.LAZY, mappedBy = "delivery", cascade = CascadeType.ALL)
private List<Plant> plants;
/* rest of class unchanged */
}
```
**RecipientAndPrice.java projection class**
```java
public class RecipientAndPrice {
private String recipientName;
private BigDecimal price;
//You'll probably need a constructor like this so CriteriaBuilder can create
public RecipientAndPrice(String recipientName, BigDecimal price) {
this.recipientName = recipientName;
this.price = price;
}
/* getters and setters */
}
```
**DeliveryRepository.java demonstrating both examples**
```java
@Repository
@Transactional
public class DeliveryRepository {
@PersistenceContext
EntityManager entityManager;
public List<Delivery> findDeliveriesByName(String name){
TypedQuery<Delivery> query = entityManager.createNamedQuery("Delivery.findByName", Delivery.class);
query.setParameter("name", name);
return query.getResultList();
}
// One possible way to solve this - query a list of Plants with deliveryId matching
// the one provided and sum their prices.
public RecipientAndPrice getBill(Long deliveryId) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<RecipientAndPrice> query = cb.createQuery(RecipientAndPrice.class);
Root<Plant> root = query.from(Plant.class);
query.select(
cb.construct(
RecipientAndPrice.class,
root.get("delivery").get("name"),
cb.sum(root.get("price"))))
.where(cb.equal(root.get("delivery").get("id"), deliveryId));
return entityManager.createQuery(query).getSingleResult();
}
/* rest of class unchanged */
}
```
Resultant Hibernate-generated Query from CriteriaBuilder:
```sql
Hibernate:
select
delivery1_.name as col_0_0_,
sum(plant0_.price) as col_1_0_
from
plant plant0_ cross
join
delivery delivery1_
where
plant0_.delivery_id=delivery1_.id
and plant0_.delivery_id=3
```
Some sample test methods you could use:
```java
@RestController
@RequestMapping("/delivery")
public class DeliveryController {
@Autowired
DeliveryService deliveryService;
@PostMapping
public Long scheduleDelivery(@RequestBody Delivery delivery) {
return deliveryService.save(delivery);
}
@GetMapping("/bill/{deliveryId}")
public RecipientAndPrice getBill(@PathVariable Long deliveryId) {
return deliveryService.getBill(deliveryId);
}
}
@Service
public class DeliveryService {
@Autowired
DeliveryRepository deliveryRepository;
public RecipientAndPrice getBill(Long deliveryId){
return deliveryRepository.getBill(deliveryId);
}
public Long save(Delivery delivery) {
delivery.getPlants().forEach(plant -> plant.setDelivery(delivery));
deliveryRepository.persist(delivery);
return delivery.getId();
}
}
```
我的GitHub練習連結:
# 13 Repository Pattern
## Udaciy
**Repository Pattern**
{%youtube ffmPznccpOY%}
Repository pattern is a way of thinking about your database as a collection of objects.
**Simple Repository Interface**
```java
public interface HumanoidRepository {
Humanoid save(Humanoid h);
Humanoid findById(Long id);
void delete(Humanoid h);
}
```
**Sample Implementation**
```java
@Repository
public class HumanoidRepositoryImpl implements HumanoidRepository {
@Autowired
EntityManager entityManager;
@Override
public Humanoid save(Humanoid h) {
if(h.getId() == null || h.getId() <= 0) {
entityManager.persist(h);
} else {
h = entityManager.merge(h);
}
return h;
}
@Override
public Humanoid findById(Long id) {
return entityManager.find(Humanoid.class, id);
}
@Override
public void delete(Humanoid h) {
if (entityManager.contains(h)) {
entityManager.remove(h);
} else {
entityManager.remove(entityManager.merge(h));
}
}
}
```

**Repository Management**
{%youtube 0GYSC1bFVzs%}
**Unique Interface, Unique Implementation**
* More customizable, able to limit methods per Entity.
* Lots of very similar interfaces and classes.
**Generic Interface, Unique Implementation**
* Fewer Interfaces, but implement unused methods.
**Generic Interface, Generic Implementation**
* Most work up front.
* Least redundant code.
## ShannonNote
簡單來說就是要你學會使用Repository應該怎麼設計
以簡單的方式來說,我們可以設計一個interface 裡面包含所有CRUD的method,然後時做一個implement那個interface的class,有點類似HashMap的架構一般。
但是這種根據Entity分成很多repository的interface就會很攏常然後重複性很高,因此下一張會教你很好用的工具協助你build repository pattern,那就是我們之前使用過的`CrudRepository`。
# 14 Spring Data JPA
{%youtube v4T6Jd6_q0s%}
Spring Data JPA provides code and code-generation tools to make it easier to use JPA. To start with Spring Data JPA, simply extend one of the Spring Data interfaces. This example allows us to access CRUD operations related to Outfits:
**OutfitRepository.java**
```java
@Repository
public interface OutfitRepository extends CrudRepository<Outfit, Long> {
}
```
Now we can simply inject this interface into our service classes and use it:
**OutfitService.java**
```java
@Service
public class OutfitService {
@Autowired
OutfitRepository outfitRepository;
public void eatOutfit(Outfit outfit){
outfitRepository.save(outfit);
}
public Outfit expelOutfit(Long id){
return outfitRepository.findById(id).orElse(null);
}
}
```
{%youtube C74rEJ6nm5w%}
To expand the repository, you can add new methods that will automatically be implemented:
```java
@Repository
public interface OutfitRepository extends CrudRepository<Outfit, Long> {
//finds a single outfit by attribute
Outfit findByHat(String hat);
//you can use Operators like And/Or, Lessthan/greaterthan, null/notnull
Outfit findByHatAndShoes(String hat, String shoes);
}
```
**Referencing Associations, Providing JPQL, and using Named Queries**
```java
@Repository
public interface HumanoidRepository extends JpaRepository<Humanoid, Long> {
//you can reference associations and attributes by chaining
//attribute names. Here we reference Humanoid.outfits.hat
List<Humanoid> findAllByOutfitsHat(String hat);
//you can provide specific JPQL Queries
@Query("select h from Humanoid h where :outfit member of h.outfits ")
List<Humanoid> findAllByOutfit(@Param("outfit") Outfit outfit);
//does the same as above
List<Humanoid> findAllByOutfitsContaining(Outfit outfit);
//automatically uses query named Humanoid.findAllNamedQuery
List<Humanoid> findAllNamedQuery(Outfit outfit);
}
```
**JpaRepository**
Extension of CrudRepository that provides some other JPA-specific methods, such as `getOne`, which returns an Entity reference, just like `entityManager.getReference`.
Spring Data JPA is not part of Hibernate, so you should look for the documentation on Repositories specifically on the Spring Data [JPA Documentation site](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories).
# 15 JPA Exercise 3
**JPA Exercise 3**
For this exercise, we’ll create our PlantRepository as a Spring Data JPA Repository. Make a new Interface called PlantRepository that extends JpaRepository for Plant Entities.
Add a method that allows you to find out if a specific plant has been delivered. It should take a Long plantId and return a Boolean. There are at least a few ways to approach this problem, so feel free to experiment. Bonus points if you come up with more than one! You may find it helpful to set a default value for the ‘completed’ attribute of Delivery to avoid returning ‘null’ values.
Add another method that returns all the plants cheaper than the specified price. It should take a BigDecimal price and return a List of Plants.
Create or update your PlantService to inject an instance of your PlantRepository. Use it to create service methods that do the following operations:
Save a new Plant and return its Id
Check if a plant has been delivered
Find a list of plants cheaper than a specified amount
Remember that you already have a DeliveryRepository that can find Delivery objects, so feel free to add service or controller methods that let you modify Delivery objects if it will help you test. If you make a Controller method that returns Plant objects, don’t forget to use your PlantDTO or mark the method with the `@JsonView` you created earlier to avoid infinite recursion from our bi-directional plant/delivery relationship.
# 16 Solution:JPA Exercise 3
Excellent! We’re able to easily add new functionality to our Services using these Repositories.
**PlantRepository.java**
```java
@Repository
public interface PlantRepository extends JpaRepository<Plant, Long> {
//check if a plant by this id exists where delivery has been completed
Boolean existsPlantByIdAndDeliveryCompleted(Long id, Boolean delivered);
//you can return a primitive directly
@Query("select p.delivery.completed from Plant p where p.id = :plantId")
Boolean deliveryCompleted(Long plantId);
//to return a wrapper class, you may need to construct it as a projection
@Query("select new java.lang.Boolean(p.delivery.completed) from Plant p where p.id = :plantId")
Boolean deliveryCompletedBoolean(Long plantId);
//we can do this entirely with the method name
List<Plant> findByPriceLessThan(BigDecimal price);
}
```
**PlantService.java**
```java
@Service
public class PlantService {
@Autowired
PlantRepository plantRepository;
public Long save(Plant plant){
return plantRepository.save(plant).getId();
}
public Boolean delivered(Long id){
// return plantRepository.deliveryCompleted(id);
return plantRepository.existsPlantByIdAndDeliveryCompleted(id, true);
}
public List<Plant> findPlantsBelowPrice(BigDecimal price) {
return plantRepository.findByPriceLessThan(price);
}
}
```
**Example PlantController.java for testing**
```java
@RestController
@RequestMapping("/plant")
public class PlantController {
@Autowired
private PlantService plantService;
@GetMapping("/delivered/{id}")
public Boolean delivered(@PathVariable Long id) {
return plantService.delivered(id);
}
@GetMapping("/under-price/{price}")
@JsonView(Views.Public.class)
public List<Plant> plantsCheaperThan(@PathVariable BigDecimal price) {
return plantService.findPlantsBelowPrice(price);
}
}
```
# 17 Flushing and Transactions
**Flushing and Transactions**
{%youtube PRyPh55b8Fw%}
**Definitions**
* Flushing: The process of synchronizing the state of the persistence context with the underlying database.
* Transaction: A set of operations that either succeed or fail as a group.
* Level 1 Cache: The Persistence Context functions as a Level 1 Cache, because it does not write changes to the database until Flushing occurs.
**Flushing Triggers**
1. Transaction Ends
2. Query overlaps with queued Entity actions
3. Native SQL Query executes without registering affected Entities
**Transactions**
If we execute multiple persistence operations, a failure on one could leave the Database in an inconsistent state. By wrapping multiple operations in a Transaction, no changes will be applied unless all operations succeed.
A good practice is to start one Transaction for each request that interacts with the database. The simplest way to do this in Spring is through the @Transactional annotation. You can annotate methods to begin a transaction when the method starts and close it when you leave. You can also annotate classes to treat all their methods as @Transactional. This annotation is best done at the Service layer, so a new transaction is started whenever the Controller classes request operations that may involve the database.
**Transactional example**
```java
@Transactional
public void createOutfitForPerson(Outfit outfit, Long personId) {
outfitRepository.save(outfit);
//getOne throws EntityNotFoundException if personId doesn't exist!
humanoidRepository.getOne(personId).getOutfits().add(outfit);
}
```
Additional Resources
[Hibernate Documentation on Flushing](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#flushing)
## ShannonNote
* 如果你怕在crud的過程中發生任何問題還可以rollback可以使用`@Transactional` 他可以使用在class或是method層面,如果使用在class代表這個類裡面的所有方法都不能有任何錯誤才可以flushing進database裡面
* 應該寫在Service裡面
* 在transaction之前,會先進行一次AUTO FLUSH,這個部分你可以看上面連結的documentation
* 好處
* 因為tansactional,導致使得persistence context變得很像一個存放暫存資料的地方
* 也不需要一直重新啟用enetitymanager
* 不會一直抓著resources不放而導致application的scalability延展性減低
* `AUTO` 何時會自動flush?
* 當transaction.commit之後就會flush一次
* 當jpql/hql寫完之後發生重疊 像是多了新物件
* 或是使用native sql
# 18 Java Persistence API Recap
{%youtube vfdDhOXCS8Q%}
**Lesson Outline**
* Persistence Context
* Entity Manager
* Queries and JPQL
* Projections
* Repository Pattern
* Spring Data JPA
* Transactions and Flushing
**Definitions:**
1. Java Persistence API (JPA)
A specification describing how to manage relational data
2. Hibernate
An implementation of the JPA Specification
3. Persistence Context
Describes the relationship between all the Entity instances in our program and their representations in the underlying database.
4. Instance
A specific copy of an Entity in program memory.
5. Persistence Context Entity States
* Transient: not associated with the persistence context. Often has not yet had an ID assigned.
* Managed: persistent. Managed by the current persistence context. Changes to the entity will be reflected in the backing database.
* Detached: previously managed. Occurs to all managed entities when persistence context ends.
* Removed: scheduled to be removed from the database. Java object still exists and has ID.
6. Entity Manager
Class that manages the persistence state of Entities in the Persistence Context
7. Entity Manager Operations
* Persist: Takes an Entity not yet managed. The Entity becomes managed and will be saved to the database.
* Find: Looks up an id in the database and returns a managed Entity.
* Merge: Updates an Entity that is in the detached state. Returns an instance of that Entity that is now managed. If Entity was not found in the database to update, persists Entity as a new row.
* Remove: Detaches an entity and deletes it from the database.
8. Lazy Loading
A way to prevent associated Entities from being retrieved until they are referenced.
9. FetchType.EAGER
Always retrieve the associated values as part of the Entity retrieval. Default value for `@ManyToOne` and `@OneToOne`.
10. FetchType.LAZY
Wait to retrieve associated values until they are referenced. Default value for `@OneToMany` and `@ManyToMany`.
11. CascadeType
Specifies which persistence operations should apply to associated entities when executed on the containing entity.
簡單來說就是主關聯的變動,其他跟主table連的table要不要變動
12. CRUD
Short for 'Create', 'Read', 'Update', 'Delete', the four main categories of basic database operations.
13. JPQL
Java Persistence Query Language. A query language very similar to SQL that can be used to reference Entities and their attributes directly.
14. Named Queries
Query strings that are defined in a class-level annotation that are validated on application launch.
15. Projections
Results of queries that are loaded into objects other than Entities.
16. Repository Pattern
A way of thinking about your database as a collection of objects. Exposes methods similar to Collection interfaces like Map or Set.
17. @Repository Annotation
A specialization of the Spring `@Component` annotation. Marks a class for component scanning by Spring and also indicates that persistence exceptions thrown by the class should be translated into Spring exceptions.
18. Spring Data JPA Repository
An interface specifying default repository behavior. Extending these interfaces allows Spring Data to automatically generate implementations based on interface method names.
像是CrudRepository或是JpaRepository
19. Flushing
The process of synchronizing the state of the persistence context with the underlying database. Triggered by:
* Transaction Ends
* Query overlaps with queued Entity actions
* Native SQL Query executes without registering affected Entities
20. Transaction
A set of operations that either succeed or fail as a group.
21. Level 1 Cache
An intermediate layer into which changes can be written and objects retrieved quickly. The Persistence Context functions as a Level 1 Cache, because it does not write changes to the database until Flushing occurs.
# 架構整理
- 02-03 透過EntityManager去做基本的CRUD
- 04 學會LAZY跟EAGER來控制資料庫何時該存取資料才不會造成負荷
- 05 了解CASCADE,剛主關聯表有發生改變時,相關的關聯表應該如何行為
- 08 學會怎麼用比較複雜的Query寫法像是distinct或是where X member of Y等等
- 09 我們那些複雜的Query可以放在@QueryName,可是透過entityManager執行的時候得與用CreateNameQuery來放
- 10 學回如何把資料庫抓取的資料塞到資料庫裏面沒有但是,是我們自己創出來的object
-
# 補充01: SELECT DISTINCT (SQL SELECT DISTINCT Statement)
參考: https://www.fooish.com/sql/distinct.html
如果你使用distint來查詢某特定欄位的結果,舉個例子來說:
目前資料庫長這樣
> TABLE: RODERS
```typescript=
===============================
ID | ORDERID | RECEIPT | PRICE
===============================
1 | R1001 | 沙拉加蛋 | 130
2 | R1001 | 蘋果派對 | 50
3 | R1002 | 沙拉加蛋 | 130
4 | R1002 | 番茄炒蛋 | 80
```
> 如果你使用SQL
```sql
SELECT DISTINCT RECEIPT FROM ORDERS
```
你可能會看到
> TABLE: RODERS
```typescript=
=========
RECEIPT
=========
沙拉加蛋
蘋果派對
番茄炒蛋
```
* 你會發現沙拉加蛋應該要有兩個,對沒錯,但因為你使用DISTINCT,所以他會把重複地給除掉,如果你使用的SQL語法是顯示出兩個欄位,那一定要兩個欄位都重複
# 補充02: @Transcation
參考: https://www.itread01.com/content/1550054167.html
* 他有幾個重要的點
* 首先他必須使用在public method才有效
* 他主要的效用是,過程中如果噴錯,資料庫不會受到影響
* 舉個例子來說,A轉帳給B,A轉過去的過程中噴錯了,結果A的存款少了但B也沒收到錢,因此才需要`@Transcational`
# 補充03: Criteria
簡單來說Criteria就是一個不需要寫SQL,只要透過java方法就可以執行的query. 用法其實也不難?
1. 首先要透過`entityManager.getCriteriaBuilder()`又或是`entityManagerFactory.getCriteriaBuilder()`來取得CriteriaBuilder instance,
2. 下一個步驟是取得`javax.persistence.criteria.CriteriaQuery`,有三個方法可以取得CriteriaQuery
* `<T> CriteriaQuery <T> createQuery(Class<T> resultClass)`
* `CriteriaQuery<Tuple> createTupleQuery()`
* `CriteriaQuery<Object> createQuery()`
## Select an Entity 可以回傳entity的方式
```java
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
//passing the Person.class in the createQuery references as the results of the query will be Person objects 表示回傳的型態
CriteriaQuery<Person> criteria = builder.createQuery( Person.class );
// 我們要從哪裡開始抓資料
Root<Person> root = criteria.from( Person.class );
//透過select 來表示要顯示那些欄位
criteria.select( root );
//where來篩選
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
//在使用createQuery把查好的內容criteria放進去,去取得list<person>的概念
List<Person> persons = entityManager.createQuery( criteria ).getResultList();
```
## Selecting an Expression 可以回傳某個特別欄位 普通型態String, int...
```java
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
//因為你只需要回傳String,因此class設定成String
CriteriaQuery<String> criteria = builder.createQuery( String.class );
//nickname要從Person這個entity裡面取得
Root<Person> root = criteria.from( Person.class );
//你只選擇要看nickname欄位
criteria.select( root.get( Person_.nickName ) );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
//因此回傳的list也是String type
List<String> nickNames = entityManager.createQuery( criteria ).getResultList();
```
## Selecting mutiple values
```java
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
//期望會回傳一個object陣列
CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class);
//從person取得資料
Root<Person> root = criteria.from(Person.class);
Path<Long> idPath = root.get(Person_.id);
Path<String> nickNamePath = root.get(Person_.nickName);
criteria.select(builder.array(idPath, nickNamePath));
criteria.where(builder.equal(root.get(Person_.name), "John Doe"));
List<Object[]> idAndNickNames = entityManager.createQuery(criteria).getResultList();
```
> 還有很多可以自己看看
## 想要把欄位放到自訂DTO ojbect
```java
public RecipientAndPriceDTO getRecipientAndPrice(Long deliveryid){
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<RecipientAndPriceDTO> criteria = criteriaBuilder.createQuery(RecipientAndPriceDTO.class);
//要取得Plant.class的任何內容都要透過root
Root<Plant> root = criteria.from(Plant.class);
//建構class instance裡面的參數要放name跟price,price要特別經過criteriaBuilder.sum
criteria.select(criteriaBuilder.construct(
RecipientAndPriceDTO.class,
root.get("delivery").get("name"),
criteriaBuilder.sum(root.get("price"))))
.where(criteriaBuilder.equal(root.get("delivery").get("id"), deliveryid));
return entityManager.createQuery(criteria).getSingleResult();
}
```
# 補充04: JpaRepository查詢
參考: https://blog.csdn.net/fly910905/article/details/78557110
JpaRepository除了有實現基本的Crud方法之外,他還有個就是可以透過方法名稱來查詢的方式去實現sql語法。
像是我想透過名字去找user,可以設計一個方法叫做findByName(String name)