Spring Data JPA 學(xué)習(xí)筆記

說(shuō)明

首先來(lái)說(shuō)JPA是一個(gè)持久化規(guī)范,也就是說(shuō)當(dāng)我們用JPA的時(shí)候我們不需要去選面向Hibernate的api編程了,這樣就大大降低了偶和度了
這里介紹spring配置版和springboot配置版(推薦)。


spring配置版

引入

JPA是一種規(guī)范,那么它的編程有哪些要求呢?
引入下載的jar包導(dǎo)入lib文件夾,然后我們的在src下面加上一個(gè)META-INF目錄在該文件夾下面加上一個(gè)persistence.xml文件,這個(gè)文件的規(guī)范寫法

persistence.xml

<?xml version="1.0"?>
<persistence 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_1_0.xsd" version="1.0">
<persistence-unit name="jun" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <property name="hibernate.connection.driver_class" value="org.gjt.mm.mysql.Driver"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.url" value="jdbc:mysql:///jpa??autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false"/>
            <property name="hibernate.max_fetch_depth" value="3"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.jdbc.fetch_size" value="18"/>
            <property name="hibernate.jdbc.batch_size" value="10"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="false"/> 
        </properties>
    </persistence-unit>
</persistence>

這個(gè)文件中的寫話我們可以再hibernate的jpa實(shí)現(xiàn)包里面找到對(duì)應(yīng)的例子,這里我是使用的是hibernate來(lái)實(shí)現(xiàn)JPA實(shí)現(xiàn),上面的配置也都和heibernate差不太多值得注意的:

<persistence-unit name="jun" transaction-type="RESOURCE_LOCAL">
  1. Persistence-unit持久性化單元,我們可以隨便命名但是在后面回到他的,通過(guò)它來(lái)找到相關(guān)的配置信息
  2. transaction-type 是指事物的類型,在通常情況下我們是本地的,但是當(dāng)我們遇到兩個(gè)數(shù)據(jù)庫(kù)保存的時(shí)候 我們會(huì)到到JTA事物來(lái)控制事務(wù),也就是二次提交

實(shí)體類來(lái)映射數(shù)據(jù)表

Persion.java

@Entity //就是告訴JPA我是一個(gè)實(shí)體bean
@Table(name="t_person")//作為修改表名,默認(rèn)情況是類名
public class Person {
    /**
    * GenerationType.AUTO它會(huì)根據(jù)數(shù)據(jù)庫(kù)方言來(lái)選擇正確的生成策略
    * 默認(rèn)情況下就是AUTO
    */
    @Id//映射主鍵
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
    // 映射一列到數(shù)據(jù)庫(kù)中 length是指字段的長(zhǎng)度
    // nullable是指是否為空,默認(rèn)情況是空
    // 當(dāng)我們不想讓類字段名與數(shù)據(jù)庫(kù)名一樣的時(shí)候用到的
    @Column(length=10,nullable=false,name="personname")
    private String name;
    // 映射日期類型TemporalType有好幾種映射方式
    // 我們可以根據(jù)自己的需求選擇正確的映射方式
    @Temporal(TemporalType.DATE)
     @Column(nullable=false)
    private Date birthday;
    // 用枚舉類型來(lái)映射性別EnumType有好幾種映射方式
    // 這里使用的是枚舉的String類型,我們也一個(gè)選擇枚舉的索引
    @Enumerated(EnumType.STRING)
    @Column(nullable=false,length=5)
    private Sex sex = Sex.MEN;
     // 對(duì)應(yīng)大文本和圖片就是二進(jìn)制
    @Lob 
    private String info;
    // 支持延遲加載(減少內(nèi)存的消耗)
    @Lob @Basic(fetch=FetchType.LAZY)
    private Byte[] image;
    // 不想這個(gè)字段與數(shù)據(jù)庫(kù)表映射
    @Transient
    private String imagepath;
    // 版本標(biāo)識(shí) 防止臟讀
    @Version
    private Integer version;
    // get set //
}

利用jpa來(lái)操作數(shù)據(jù)庫(kù)

Test.java

@Test
public void testSave() {
    /**
    * jun是在persistence.xml中持久化單元名稱
    */
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    //--->>SessionFactory-->>session-->>begin
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();//開啟事物
    Person person = new Person("劉文軍");
    person.setBirthday(new Date());
    em.persist(person);
    System.out.println("----->>"+person.getSex().getName());
    em.getTransaction().commit();
    em.close();
    factory.close();
}
  1. 這里我們EntityManagerFactory就相當(dāng)于Hibernate中的SessionFactory,
    然而EntityManager就相當(dāng)于Hibernate中的Session。
  2. 在JPA中我們獲得它的方式是Persistence.createEntityManagerFactory("jun");
    jun是在persistence.xml中持久化單元名稱
  3. EntityManager em = factory.createEntityManager();
    其中EntityManager中的幾種操作數(shù)據(jù)庫(kù)方法有
    Persist(),find(),getReference(),merge(),remove()這里我就列出幾種常見的
  4. 這里說(shuō)有的getReference()就相當(dāng)于Hibernate中的Load() 支持Lazy加載,
    getReference它會(huì)查找一個(gè)代理對(duì)象,當(dāng)訪問(wèn)它的屬性的時(shí)候它才會(huì)向數(shù)據(jù)庫(kù)操作
  5. 但是值得注意的這個(gè)必須確保Session是打開狀態(tài)

實(shí)體bean的幾種狀態(tài)

一、新建狀態(tài)

new一個(gè)實(shí)體的時(shí)候它所處于的狀態(tài)

二、托管狀態(tài)

這個(gè)狀態(tài)非常重要,當(dāng)一個(gè)實(shí)體bean是托管狀態(tài)的時(shí)候我們修改它的屬性值得話,即使我們沒(méi)有用到hibernate中的相關(guān)方法操作數(shù)據(jù)庫(kù),她也會(huì)同步到數(shù)據(jù)庫(kù)里的,托管就是從數(shù)據(jù)庫(kù)里面查詢到的一個(gè)對(duì)象,記住,托管狀態(tài)必須的和事物關(guān)聯(lián)上來(lái)

    em.getTransaction().begin();
    person.setName("小劉");
    em.getTransaction().commit();
    em.close();
    factory.close();

三、游離狀態(tài)

當(dāng)我們使用EntityManager中的clear()方法的時(shí)候,它會(huì)把事物管理器中的所有實(shí)體變成游離狀態(tài),
處在游離狀態(tài)的實(shí)體bean,我們修改它的屬性它不會(huì)同步到數(shù)據(jù)庫(kù)中的

    em.getTransaction().begin();
    em.clear(); // 把實(shí)體管理器中的所有實(shí)體都變成游離狀態(tài)
    person.setName("老劉");
    em.merge(person);
    em.getTransaction().commit();
    em.close();
    factory.close();

四、關(guān)閉狀態(tài)

就是當(dāng)我們把Session關(guān)閉的時(shí)候,實(shí)體bean處于的狀態(tài)

JPA中使用EJQL語(yǔ)句

查詢

public void query() {
    /**
    * jun是在persistence.xml中持久化單元名稱
    */
    EntityManagerFactory factory=Persistence.createEntityManagerFactory("jun");
    //--->>SessionFactory-->>session-->>begin
    EntityManager em = factory.createEntityManager();
    Query query = em.createQuery("select o from Person o where o.id=?1");
    query.setParameter(1, 2);
    //
    /**
    * Person person = (Person)query.getSingleResult();
    * 這個(gè)就相當(dāng)于Hibernate中的uniqueResult();
    * 如果沒(méi)有查詢到數(shù)據(jù)的話會(huì)出錯(cuò)的,所有我們一般不這樣做
    * 我們先得到List 然后遍歷它
    */
    List<Person> list = query.getResultList();
    for(Person person : list) 
    System.out.println("---------->>" + person.getName());
    query = em.createQuery("select count(o) from Person o ");
    Long count = (Long)query.getSingleResult();
    System.out.println("---------->>" + count);
    em.close(); 
    factory.close();
}

刪除查詢(記得要開啟事物)

public void deletequery() {
    /**
    * jun是在persistence.xml中持久化單元名稱
    */
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Query query = em.createQuery("delete from Person o where o.id=?1");
    query.setParameter(1, 2);
    query.executeUpdate();
    em.getTransaction().commit();
    em.close(); 
    factory.close();
}

修改查詢(記得要開啟事物)

public void updatequery() {
    /**
    * jun是在persistence.xml中持久化單元名稱
    */
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Query query = em.createQuery("update from Person o set o.name=:name where o.id=:id");
    query.setParameter("name", "小劉");
    query.setParameter("id", 3);
    query.executeUpdate();
    em.getTransaction().commit();
    em.close(); 
    factory.close();
}

刷新方法

public void testgetPerson() {
    /**
    * jun是在persistence.xml中持久化單元名稱
    */
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    //--->>SessionFactory-->>session-->>begin
    EntityManager em = factory.createEntityManager();
    Person person = em.find(Person.class, 1);
    /**
    * 1分鐘內(nèi)
    * 比方說(shuō)這里有人直接操作數(shù)據(jù)庫(kù)改了數(shù)據(jù),我們?nèi)绾蝸?lái)得到person呢
    * 有人說(shuō)我們可以再執(zhí)行一下find方法,這個(gè)事不可以的,因?yàn)槟阍俅?    * 執(zhí)行find方法的時(shí)候EntityManager會(huì)中一級(jí)緩存中取得id是1的
    * Person數(shù)據(jù),也就是說(shuō)不能得到最新的數(shù)據(jù),那么如果我們想得到最
    * 新的數(shù)據(jù)怎么辦呢?JPA幫我們做了一個(gè)刷新的方法em.refresh(person);
    * 通過(guò)它我們就可以得到最新的數(shù)據(jù)了
    */
    em.refresh(person);
    System.out.println("----->>"+person.getName());
    em.close();
    factory.close();
}

JPA中實(shí)現(xiàn)表間關(guān)聯(lián)關(guān)系

一的多

@Entity
@Table(name="t_order")
public class Order {

    @Id
    @Column(length=200)
    private String orderid;
    @Column(nullable=false)
    private Float amount = 0f;
    /**
     * cascade=CascadeType.REFRESH設(shè)置級(jí)聯(lián)刷新
     * CascadeType.PERSIST設(shè)置級(jí)聯(lián)保存
     * CascadeType.MERGE設(shè)置級(jí)聯(lián)更新
     * CascadeType.REMOVE設(shè)置級(jí)聯(lián)刪除
     * CascadeType.ALL設(shè)置四種級(jí)聯(lián)
     * 這四種級(jí)聯(lián)都是對(duì)應(yīng)的四種方法才能起作用PERSIST(),MERGE(),REMOVE(),REFRESH()
     * fetch=FetchType.LAZY支持LAzy加載,只有用到該屬性的時(shí)候才會(huì)讀集合數(shù)據(jù)
     * 注意這時(shí)我們必須保持EntityManager是開著的,默認(rèn)是延遲加載
     * mappedBy="order"指定OrderItem類里面的order這個(gè)屬性來(lái)維護(hù)關(guān)系
     */
    @OneToMany(cascade={CascadeType.ALL},
                    fetch=FetchType.LAZY,
                    mappedBy="order")
    private Set<OrderItem> items = new HashSet<OrderItem>();

    // get set //
}

為了我們添加Set集合方便我們通常的情況下我們的寫一個(gè)添加Set的方法

public void addOrderItem(OrderItem orderItem) {
    orderItem.setOrder(this);//this就是Order
    items.add(orderItem);
}

多的一

@Entity
@Table(name="t_orderitem")
public class OrderItem {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(length=40,nullable=false)
    private String productName;
    @Column(nullable=false)
    private Float sellPrice = 0f;
    /**
     * optional=false指定該字段不能空
     * @JoinColumn(name="order_id")指明關(guān)聯(lián)關(guān)系的子段名
     * 即定義外鍵的名稱
     *   
     * */
    @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
    @JoinColumn(name="order_id")
    private Order order;
    // get set //
}

測(cè)試

public void testCreate() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Order order = new Order();
    order.setAmount(598f);
    order.setOrderid(UUID.randomUUID().toString());
    OrderItem orderItem1 = new OrderItem();
    orderItem1.setProductName("籃球");
    orderItem1.setSellPrice(256f);
    order.addOrderItem(orderItem1);
    OrderItem orderItem2 = new OrderItem();
    orderItem2.setProductName("女人");
    orderItem2.setSellPrice(800000f);
    order.addOrderItem(orderItem2);
    em.persist(order);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

這里我們并沒(méi)有保存orderIterm
但是我們?cè)跀?shù)據(jù)庫(kù)中依然能看到orderIterm的數(shù)據(jù)
保存到數(shù)據(jù)庫(kù)里面了,這里就是設(shè)置級(jí)聯(lián)保存的緣故
CascadeType.PERSIST設(shè)置級(jí)聯(lián)保存

一對(duì)一

Person.java

@Entity
@Table(name="t_person")
public class Person {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(length=10,nullable=false)
    private String name;
    @OneToOne(optional=false,cascade={CascadeType.ALL})
    @JoinColumn(name="idcard_id")
    private IDCard idCard;
    public Person() {}
    public Person(String name) {
        this.name = name;
    }
}

IDCard.java

@Entity
@Table(name="t_idcard")
public class IDCard {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(length=18,nullable=false)
    private String cardno ;
    @OneToOne(mappedBy="idCard",
            cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE})
    private Person person;
    public IDCard() {}
    public IDCard(String cardno) {
    this.cardno = cardno;
    }
}

測(cè)試

public void testCreateTable() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Person person = new Person("小劉");
    IDCard idCard = new IDCard("411521198409131915");
    idCard.setPerson(person);
    person.setIdCard(idCard);
    em.persist(person);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

多對(duì)多

Student.java

@Entity
@Table(name="t_student")
public class Student {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(length=10,nullable=false)
    private String name;
    /**
     * @JoinTable(name="t_stu_tea",
            joinColumns=@JoinColumn(name="student_id"),
            inverseJoinColumns=@JoinColumn(name="teacher_id"))
        創(chuàng)建中間表來(lái)維護(hù)兩個(gè)表的關(guān)系
     * joinColumns=@JoinColumn(name="student_id")定義維護(hù)端在中間表中的關(guān)聯(lián)字段
     * inverseJoinColumns=@JoinColumn(name="teacher_id")定義被維護(hù)端在中間表中的關(guān)聯(lián)字段
     * 
     */
    @ManyToMany(cascade=CascadeType.REFRESH)
    @JoinTable(name="t_stu_tea",
            joinColumns=@JoinColumn(name="student_id"),
            inverseJoinColumns=@JoinColumn(name="teacher_id"))
    private Set<Teacher> teachers = new HashSet<Teacher>();
    public Student() {}
    public Student(String name) {
        this.name = name;
    }
}
  • 我們通常把set集合方法重寫
public void addTeacher(Teacher teacher) {
    this.teachers.add(teacher);
}
public void removeTeacher(Teacher teacher) {
    if(this.teachers.contains(teacher)) {
    this.teachers.remove(teacher);
    }
}

Teacher.java

@Entity
@Table(name="t_teacher")
public class Teacher {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(length=12,nullable=false)
    private String name;

    @ManyToMany(cascade=CascadeType.REFRESH,mappedBy="teachers")
    private Set<Student> students = new HashSet<Student>();

    public Teacher() {}

    public Teacher(String name) {
        this.name = name;
    }
}

測(cè)試

@Test
public void testCreateTable() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Student student = new Student("小劉");
    Teacher teacher = new Teacher("肖老師");
    em.persist(student);
    em.persist(teacher);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

/**
* 建立學(xué)生跟老師的關(guān)系
*/
@Test
public void buildTS() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Student student = em.find(Student.class, 1);
    student.addTeacher(em.getReference(Teacher.class, 1));
    em.getTransaction().commit();
    em.close();
    factory.close();
}

/**
* 解除學(xué)生跟老師的關(guān)系
*/
@Test
public void deleteTS() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    Student student = em.find(Student.class, 1);
    student.removeTeacher(em.getReference(Teacher.class, 1));
    em.getTransaction().commit();
    em.close();
    factory.close();
}

/**
* 刪除老師
*/
@Test
public void deleteTeacher() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    /**
    * 刪除老師的時(shí)候一定的解除掉老師和學(xué)生的關(guān)系
    */
    Student student = em.find(Student.class, 1);
    student.removeTeacher(em.getReference(Teacher.class, 1));
    em.remove(em.getReference(Teacher.class, 1));
    em.getTransaction().commit();
    em.close();
    factory.close();
}

/** 
*刪除學(xué)生
*/
@Test
public void deleteStudent() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();

    /**
    * 這個(gè)為什么不需要解除關(guān)系呢,主要是因?yàn)閷W(xué)生這一段是維護(hù)端
    */
    Student student = em.find(Student.class, 1);
    em.remove(student);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

聯(lián)合主鍵

我們單獨(dú)寫一個(gè)類作為主鍵,但是這個(gè)類的要滿足3條規(guī)則

  1. 它必須實(shí)現(xiàn)Serializable接口
  2. 它必須重寫字段的hashCode()和equals()方法
  3. 要在類上表明@Embeddable,這個(gè)注解的意思就是讓它的屬性在AirLine也正常做屬性
    那么我們?cè)谥黧w類里面用這個(gè)主鍵類做為id,在id上面加上@EmbeddedId表明就是用主鍵類的屬性作為主鍵,并映射到數(shù)據(jù)庫(kù)表中

AirLine.java

@Entity
@Table(name="t_airline")
public class AirLine {
    @EmbeddedId
    private AirLinePK id;
    @Column(length=10,nullable=false)
    private String name;
}

AirLinePK.java

@Embeddable
public class AirLinePK implements Serializable{
    // 序列UID
    private static final long serialVersionUID = -7125628704970675246L;

    @Column(length=20)
    private String startLine;
    @Column(length=20)
    private String endLine;
    // 注意這里一定的時(shí)間他HashCode()和equals()方法
}

測(cè)試

public void testPK() {
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
    EntityManager em = factory.createEntityManager();
    em.getTransaction().begin();
    AirLine airLine = new AirLine("PEK","SHG","北京飛往上海");
    em.persist(airLine);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

使用步驟和上邊spring配置版介紹基本一致,關(guān)鍵是要理解一對(duì)一、一對(duì)多、多對(duì)一、多對(duì)多的關(guān)聯(lián)關(guān)系


推薦使用Spring Boot

配置Maven依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置JPA

# maven打包 clean compile package
spring:
    # 熱重啟
    devtools:
    restart:
      enabled: true
    # 數(shù)據(jù)源
    datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql:///qnzf?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
      username: root
      password: 

    # JPA配置
    jpa:
      show-sql: true
      hibernate:
        naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        ddl-auto: update

    # tomcat
    server:
      port: 8080
      tomcat:
        Uri-encoding: UTF-8

通過(guò)實(shí)體類映射數(shù)據(jù)表

// LED.java
@Entity
@Table(name = "tbl_led")
public class LED {
    public LED() {
        super();
    }
    // 指定id
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 唯一鍵
    @Column(unique = true)
    private Integer pin; // LED連接的針腳位
}

通過(guò)接口類繼承使用JPA內(nèi)封裝實(shí)現(xiàn)的方法

// LEDRepository.java
public interface LEDRepository extends JpaRepository<LED, Long> { }

最后可以在Controller類中使用

@Autowired
private LEDPowerRepository ledPowerRepository;

個(gè)別使用方法可以瀏覽其他教程介紹加強(qiáng)一下

Spring For All 社區(qū) Spring Data JPA 從入門到進(jìn)階系列教程
SpringBoot實(shí)戰(zhàn)SpringDataJPA
使用 Spring Data JPA 簡(jiǎn)化 JPA 開發(fā)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容