1. 程式人生 > >Spring Boot入門10

Spring Boot入門10

##回顧
Spring:IOC+AOP
IOC : 控制反轉, 物件的建立者,將物件建立的工作反轉交給另外一個類去處理
DI : 依賴注入,Spring建立物件的時候,可以幫助我們去給一些成員變數賦值
AOP : 面向切面程式設計, 主要是對程式中某一個面或者對所有service層,進行增強
動態代理: 程式動態的建立一個代理類出來,對被代理的物件進行增強
靜態代理: 真實的存在一個類

		代理的特徵:
			1.繼承同一個類或者實現同一個介面
			2.代理物件需要持有被代理的引用
			3.增強需要增強的方法
			4.不需要增強的方法直接呼叫原來的

JPA 規範 -----> Hibernate操作資料庫

一、JPA

1. JPA 介紹

JPA是Java Persistence API的簡稱,中文名Java持久層API,是JDK 5.0註解或XML描述物件-關係表的對映關係,並將執行期的實體物件持久化到資料庫中。Sun引入新的JPA ORM規範出於兩個原因:其一,簡化現有Java EE和Java SE應用開發工作;其二,Sun希望整合ORM技術,實現天下歸一 。 其實在5.0出現釋出之前,市面上有諸如、Hibernate 、OpenJPA 、TopLink 等一些列 ORM框架,並且hibernate已經很受大眾喜愛了,後來hibernate在3.2版本正式接入JPA 規範,表示自己即屬於JPA的框架的具體實現。 一句話概括: JPA 是一種規範、 Hibernate 是這種規範的最好實現。

Dao最受歡迎的兩個框架: MyBatis ----不是JPA體系 ----- 需要寫sql語句 | Hibernate — JPA 體系 -----可以不用寫sql語句

物件User ----> user表 ----> User物件

SSH : H :Hibernate

SSM : MyBatis

2. ORM介紹

Object Relational Mapping : 物件關係對映 。 ORM的思想其實就是讓 表 和 JavaBean 形成一種對映關係 , 並且讓表裡面的欄位 和JavaBean 裡面的成員形成對映關係。

在這裡插入圖片描述

3. JPA 入門

此處就以一個簡單的儲存案例來實現JPA 入門吧。

  • 新增JPA 依賴
// 在maven 搜尋關鍵字  persistence 即可  hibernate的核心依賴,會包含它對JPA規範的升級,所以這個依賴可以不加 
//compile group: 'javax.persistence', name: 'persistence-api', version: '1.0'

//不要忘記了新增mysql資料庫依賴
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.17'

//下面要新增hibernate的依賴,因為JPA 只是一套規範而已,不是具體的實現,JPA的實現有很多,
//這裡採用hibernate作為我們的實現,所以需要新增hibernate的依賴庫
//compile group: 'org.hibernate', name: 'hibernate-core', version: '4.3.9.Final'

//hibernate實體管理者,對接JPA規範的管理員  這是hibernate為了迎合JPA的規範做出來的。
compile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '4.3.9.Final'
  • 定義實體類
@Entity(name="t_user")
public class User {
    private static final String TAG = "User";

    private int id;
    private String name;
    private int age ;


    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    //剩下的get 和 set方法
    ...
}
    
  • 定義配置檔案

必須要說明一點,配置檔案的名字需要固定,必須叫:persistence.xml , 而且也必須放在 META-INF下面,不能直接放在resource下面。需要在resource下面,新建META-INF 資料夾,然後在放置 xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    


    <!--持久化單元,資料庫 & 實體類 & 具體實現方式配置單元-->
    <persistence-unit name="user">

        <!--宣告提供者,也就是說具體實現,因為JPA 只是規範,這裡使用hibernate作為具體實現-->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!--表示實體類有哪些-->
        <class>com.itheima.bean.User</class>

        <!--資料庫連線引數-->
        <properties>
            <!--使用jpa的配置-->
            <!--<property name="javax.persistence.jdbc.url" 	
								value="jdbc:mysql://localhost:3306/test"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> -->

            <!--也可以使用hibernate的配置為了以後無縫對接,還是建議使用上面的規範化的JPA 配置-->
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>

            <!--表示自動建表-->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>

    </persistence-unit>
</persistence>
  • 測試程式碼
@Test
    public  void testJPA(){

        //建立實體管理工廠 , 引數的user 來自於配置檔案的配置單元名字。
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("user");

        //建立實體管理員
        EntityManager manager = factory.createEntityManager();

        //獲取事務物件
        EntityTransaction transaction = manager.getTransaction();

        //開啟事務
        transaction.begin();

        //構建實體物件
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);


        //持久化,也就是儲存到資料庫
        manager.persist(user);
        
        //提交事務
        transaction.commit();
        
         //關閉管理員
    	manager.close();

        //關閉工廠,一般不關閉,後面也不用關心這個了。
        factory.close();
    }

4. 入門詳解

a. 註解解釋

@Entity : 表示該類是一個實體,也就是和表形成對映,可以新增屬性來指定對應的表

   如:@Entity(name="t_user")

@Table : 指定表名,可以不寫,可以在entity註解上直接表示標識表名。

  如 : @Table(name="t_user")

@Id : 用於表示主鍵,指定哪一個屬性和主鍵對應 可以在變數上宣告,也可以在get方法上宣告,建議統一。

@GeneratedValue : 用於表示主鍵生成策略 主鍵生成策略,提供的有4種

  • 主鍵策略解釋
1、AUTO 自動選擇一個最適合底層資料庫的主鍵生成策略。如MySQL會自動對應auto increment。這個是預設選項,即如果只寫@GeneratedValue,等價於@GeneratedValue(strategy=GenerationType.AUTO)。注意在mysql5 + hibernate 5的版本測試下,會產生另外一張表,用於記錄主鍵值。 可以在核心配資檔案中新增此屬性來達到native效果
	<property name="hibernate.id.new_generator_mappings">false</property>

2、IDENTITY 表自增長欄位,Oracle不支援這種方式。

3、SEQUENCE 通過序列產生主鍵,MySQL不支援這種方式。

4、TABLE 通過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略可以使應用更易於資料庫移植。不同的JPA實現商生成的表名是不同的,如 OpenJPA生成openjpa_sequence_table表,Hibernate生成一個hibernate_sequences表,而TopLink則生成sequence表。這些表都具有一個序列名和對應值兩個欄位,如SEQ_NAME和SEQ_COUNT。
  • 關於UUID策略

通常我們建表,主鍵都是int型別,但是其實在開發的時候,有些表也會建成String型別,也就是varchar型別 , 並且有時候我們要主鍵唯一,這時候需要使用到uuid策略了。JPA並沒有提供UUID 策略, 好在hibernate提供了這種策略的支援。

//宣告uuid這種策略型別。然後給定一個別名,叫做jpa-uuid  這個註解是hibernate提供的註解, jpa的註解沒有
@GenericGenerator(name="jpa-uuid" ,strategy="uuid") 

//表示對應 t_student 這個表
@Entity(name="t_student")
public class Student {

	private String id;
	private String name;
	private int age;
	private String phone;
	private String address;
	
	//指定使用你的主鍵策略是 jpa-uuid這個名稱對一個你的型別,其實就是指定了主鍵是uuid 。 
    //由hibernate來維護主鍵。
    @Id 
	@GeneratedValue(generator="jpa-uuid")
	public String getId() {
		return id;
	}
  	... 
}

b. xml配置解釋

  • xml必須放在META-INF下面,名字也必須固定是persistence.xml
  • xml其實就是用於表示怎麼連線資料庫,具體幹活的是JPA實現是什麼, 以及有哪些對映實體類。

在這裡插入圖片描述

  • 具體的屬性名稱,可以參考pdf文件。 第八章節

在這裡插入圖片描述

  • 關於hibernate的配置,請參考hibernate的pdf文件

在這裡插入圖片描述

c. 測試程式碼解釋

程式碼沒有什麼好解釋的了,必須要有實體管理員工廠,然後獲取到管理員,後續就可以操作了。 可以稍稍解釋下即可

5. CRUD

JPA 有一個要求就是寫入操作,需要使用事務,否則資料不會到達資料庫。

1. 增加

 @Test
public  void testJPA(){

    //建立實體管理工廠 , 引數的user 來自於配置檔案的配置單元名字。
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("user");

    //建立實體管理員
    EntityManager manager = factory.createEntityManager();

    //獲取事務物件
    EntityTransaction transaction = manager.getTransaction();

    //開啟事務
   transaction.begin();

    //構建實體物件
    User user = new User();
    user.setName("zhangsan");
    user.setAge(28);


    //持久化,也就是儲存到資料庫
    manager.persist(user);

    //提交事務
    transaction.commit();
    
     //關閉管理員
    manager.close();

    //關閉工廠,一般不關閉,後面也不用關心這個了。
    factory.close();
}

2. 刪除

需要刪除某條記錄,需要先把它查詢出來,然後才能刪除。

@Test
public  void testDelete(){

    //建立實體管理工廠 , 引數的user 來自於配置檔案的配置單元名字。
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("user");

    //建立實體管理員
    EntityManager manager = factory.createEntityManager();

    //獲取事務物件
    EntityTransaction transaction = manager.getTransaction();

    //開啟事務
    transaction.begin();

    //查詢主鍵為1的使用者
    User u = manager.find(User.class,1);

    //刪除使用者
    manager.remove(u);

    //提交事務
    transaction.commit();
    
    //關閉管理員
    manager.close();

    //關閉工廠,一般不關閉,後面也不用關心這個了。
    factory.close();
}

3. 修改

修改也一樣,需要先查詢,然後修改後,在持久化。

 @Test
    public  void testUpdate(){

        //建立實體管理工廠 , 引數的user 來自於配置檔案的配置單元名字。
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("user");

        //建立實體管理員
        EntityManager manager = factory.createEntityManager();

        //獲取事務物件
       EntityTransaction transaction = manager.getTransaction();

        //開啟事務
        transaction.begin();


        //查詢主鍵為1的使用者
        User u = manager.find(User.class,2);
        u.setAge(102);

        //這句可以不寫,因為查詢出來的user物件已經是持久態,直接操作它也可以修改資料庫
       // manager.persist(u);
       /* //刪除使用者
        manager.remove(u);*/

        //提交事務
       transaction.commit();
    
        //關閉管理員
    	manager.close();

        //關閉工廠,一般不關閉,後面也不用關心這個了。
        factory.close();

    }

4. 查詢

 @Test
public  void testQuery(){

    //建立實體管理工廠 , 引數的user 來自於配置檔案的配置單元名字。
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("user");

    //建立實體管理員
    EntityManager manager = factory.createEntityManager();

    //不支援寫* 
    Query query = manager.createQuery("select t from User t");

    List<User> list = query.getResultList();

    System.out.println("list=" + list);
    
    //關閉管理員
    manager.close();
    
    //關閉工廠,一般不關閉,後面也不用關心這個了。
    factory.close();
}

6. 物件時態

這個狀態主要是針對我們操作的持久化類物件。 有四種狀態

  • 瞬時態

物件僅僅是創建出來(new物件),沒有和EntityManager 產生關係

  • 持久態

和EntityManager建立關係,被持久化,儲存到資料庫或者剛從資料庫查詢出來。

  • 脫管態

已經持久化過了,現在要脫離管理。 提交事務,或者清除EntityManager都會走向這個狀態。

  • 刪除態

該狀態只有在JPA 範疇裡面才有,單獨拿hibernate來說,沒有這個狀態。只有呼叫了EntityManager

的remove方法,才會走到這個狀態。

在這裡插入圖片描述

7. 多表關係確立

最好使用例子引出關係確立的重要性。

a. 多對一

此處以分類 & 商品的關係來解釋

  • 分類 Category
@Entity(name="category")
public class Category {

    private int id;
    private String name;


    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    //剩下的get &  set方法
    ...
}
  • 商品 Product

商品在這個關係裡面扮演的是外來鍵的關係,是多方的角色,也就是一種商品分類,可以有很多件商品。所以從商品的位置出發,它和分類的關係是多對一的關係。

@Entity(name="product")
public class Product {

    private int id ;
    private String name;
    private double price;

    //表示該商品屬於哪一種分類。
    private Category category;


    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

	// @ManyToOne表示是多對一關係 optional =false 表示該外來鍵不能為空
   @ManyToOne(optional = false)
    
    //@JoinColumn name=cid 通俗的意思是: 拿什麼列來作為外來鍵,
    //這裡指定cid , 表示生成的是外來鍵名稱叫做cid
   @JoinColumn(name="cid")
   public Category getCategory() {
        return category;
    }

}

b. 一對多

一對多,比前面的多對一稍微麻煩一點,而且一對多必須雙方都配上註解,否則生成的關係會比較亂。

  • 分類 Category

區別只是一方 category 裡面多了一個註解 @OneToMany

@Entity(name="category")
public class Category {

    private int id;
    private String name;


    private Set<Product> productSet = new HashSet<Product>();

    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }


    //mappedBy 表示和誰形成對映關係,也表示誰來維護這層關係。
    @OneToMany(mappedBy = "category")
    public Set<Product> getProductSet() {
        return productSet;
    }

    //剩下的get & set 方法
    ...
}
  • 商品 Product
@Entity(name="product")
public class Product {

    private int id ;
    private String name;
    private double price;


    //表示該商品屬於哪一種分類。
    private Category category;


    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }


   @ManyToOne(optional = false)
   @JoinColumn(name="cid")
   public Category getCategory() {
        return category;
    }

    //剩下的get & set方法
    ...
}

8 . 查詢

  1. 物件導航查詢

所謂物件導航查詢的意思是: 如果A表和B表存在主外來鍵關係(一對多 | 多對一 ),那麼在查詢A表的時候, 也會順便查詢與該條記錄關聯的B表記錄資訊。如分類和商品的關係, 如果查詢手機分類,那麼會順便把商品表中屬於手機分類的商品給查詢出來。

 //建立實體工廠
EntityManagerFactory factory = Persistence.createEntityManagerFactory("demo");
//建立實體管理物件
EntityManager entityManager = factory.createEntityManager();
//查詢分類表
Category category = entityManager.find(Category.class, 3);
//列印這個分類對應的所有商品資訊(物件導航)
System.out.println(category.getProducts());
//關閉實體管理物件
entityManager.close();
//關閉工廠
factory.close(); 

二、SpringData JPA

1. 介紹

在JavaEE 5.0釋出的時候,sun公司就提出了jpa的規範,希望整合ORM技術,實現天下歸一 。 雖然我們學過的orm技術只有hibernate、但是其實orm技術還有其他的一些方案,比如Apache的 openJPA。 Spring 作為業務邏輯層框架,起到承上啟下的作用。所以它對JPA的這些技術實現做了一套封裝。大家只要按照規範來配置即可,甚至我們的dao層實現都不用實現了,它在內部給我們實現,如果我們想換到其他的jpa實現方案,那麼只需要修改配置即可。 這就是我們要說的Spring Data JPA。

JPA

Hibernate | Open Jap | Toplink …

Spring Data JPA —> 對Dao層的程式碼再一次升級封裝 。 統一的JPA的實現,在封裝。

2. 入門

####1. 搭建環境 (建表)

  • 新增依賴
//mysql驅動
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.17'

//springboot 依賴
compile("org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE")

//spring data jpa 注意: 不要引入錯了,要找的組是springboot的依賴。如下面這條註釋的是錯誤的。
//因為我們使用了SpringBoot,所以採用的庫,也要是springboot組下的。
//compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.11.3.RELEASE'


compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.5.10.RELEASE'
  • 新增配置檔案

同樣還是那個 application.properties , 位於 resources 下面

#連線資料庫
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


#hibernate配置
# 自動建表 update:表示有表則直接使用,無表就新建
spring.jpa.hibernate.ddl-auto=update

# 表示在操作時,輸出sql語句
spring.jpa.show-sql=true
  • 實體類
//name :用於表示構建出來的表名
@Entity(name="t_user")
public class User {
    private static final String TAG = "User";
    private int id;
    private String name;
    private int age ;


  
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    //剩餘的get & set方法程式碼
    ...
}    

2. 新增資料

上面僅僅是完成了表的建立工作,實際上還並沒有看出來SpringData JPA 有什麼厲害之處,現在就要往表裡面新增資料了。

  • controller
@RestController
public class UserController {

    //這裡要注入業務邏輯層引用
    @Autowired
    private UserService userService;


    @RequestMapping("/save")
    public String save(){

        User user = new User();
        user.setName("奧巴馬");
        user.setAge(58);

        //呼叫業務邏輯層
        userService.save(user);
        return "save success~!";
    }
}
  • service
public interface UserService {

    void save(User user);
}

---------------------------------------------------------------

@Service
@Transactional
public class UserServiceImpl implements UserService {

    //這裡要注入UserDao引用
    @Autowired
    private UserDao userDao;

    @Override
    public void save(User user) {
        userDao.save(user);
    }
}
    
  • dao

注意: dao層只有一個介面而已,方法沒有,其實是父類CrudRepository 已經定義了一些常用的方法,我們可以直接拿過來用即可。真正我們在業務邏輯層拿到的是 SimpleJPARepository 這個類的例項。它其實就是實現了CrudRepository介面方法。

public interface UserDao extends CrudRepository<User, Integer> {
    
}

三、 通用介面介紹

經過上面的入門例子,大家也看到了,spring data對於我們程式設計確實方便了許多。我們無需關心dao層的實現,只需要簡單的照著規則去寫程式碼即可,spring data jpa 最重要的就是我們的dao層繼承的介面。接下來給大家著重說明它的幾個介面。