String Data JPA 學習筆記

說明

首先來講JPA是一個持久化規範,也就是說當咱們用jpa的時候咱們不須要去選面向hibernate的api編程了,這樣就大大下降了偶和度了html

引入

JPA是一種規範,那麼它的編程有哪些要求呢?
引入下載的jar包導入lib文件夾,而後咱們的在src下面加上一個META-INF目錄在該文件夾下面加上一個persistence.xml文件,這個文件的規範寫法java

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>

這個文件中的寫話咱們能夠再hibernate的jpa實現包裏面找到對應的例子,這裏我是使用的是hibernate來實現JPA實現,上面的配置也都和heibernate差不太多值得注意的:mysql

<persistence-unit name="jun" transaction-type="RESOURCE_LOCAL">
  1. Persistence-unit持久性化單元,咱們能夠隨便命名可是在後面回到他的,經過它來找到相關的配置信息
  2. transaction-type 是指事物的類型,在一般狀況下咱們是本地的,可是當咱們遇到兩個數據庫保存的時候 咱們會到到JTA事物來控制事務,也就是二次提交

實體類來映射數據表

Persion.javaspring

@Entity //就是告訴JPA我是一個實體bean
@Table(name="t_person")//做爲修改表名,默認狀況是類名
public class Person {
    /**
    * GenerationType.AUTO它會根據數據庫方言來選擇正確的生成策略
    * 默認狀況下就是AUTO
    */
    @Id//映射主鍵
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
    // 映射一列到數據庫中 length是指字段的長度
    // nullable是指是否爲空,默認狀況是空
    // 當咱們不想讓類字段名與數據庫名同樣的時候用到的
    @Column(length=10,nullable=false,name="personname")
    private String name;
    // 映射日期類型TemporalType有好幾種映射方式
    // 咱們能夠根據本身的需求選擇正確的映射方式
    @Temporal(TemporalType.DATE)
     @Column(nullable=false)
    private Date birthday;
    // 用枚舉類型來映射性別EnumType有好幾種映射方式
    // 這裏使用的是枚舉的String類型,咱們也一個選擇枚舉的索引
    @Enumerated(EnumType.STRING)
    @Column(nullable=false,length=5)
    private Sex sex = Sex.MEN;
     // 對應大文本和圖片就是二進制
    @Lob 
    private String info;
    // 支持延遲加載(減小內存的消耗)
    @Lob @Basic(fetch=FetchType.LAZY)
    private Byte[] image;
    // 不想這個字段與數據庫表映射
    @Transient
    private String imagepath;
    // 版本標識 防止髒讀
    @Version
    private Integer version;
    // get set //
}

利用jpa來操做數據庫

Test.javasql

@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就至關於Hibernate中的SessionFactory,

然而EntityManager就至關於Hibernate中的Session。數據庫

  1. 在JPA中咱們得到它的方式是Persistence.createEntityManagerFactory("jun");

jun是在persistence.xml中持久化單元名稱編程

  1. EntityManager em = factory.createEntityManager();

其中EntityManager中的幾種操做數據庫方法有
Persist(),find(),getReference(),merge(),remove()這裏我就列出幾種常見的api

  1. 這裏說有的getReference()就至關於Hibernate中的Load() 支持Lazy加載,

getReference它會查找一個代理對象,當訪問它的屬性的時候它纔會向數據庫操做緩存

  1. 可是值得注意的這個必須確保Session是打開狀態

實體bean的幾種狀態

1、新建狀態

new一個實體的時候它所處於的狀態tomcat

2、託管狀態

這個狀態很是重要,當一個實體bean是託管狀態的時候咱們修改它的屬性值得話,即便咱們沒有用到hibernate中的相關方法操做數據庫,她也會同步到數據庫裏的,託管就是從數據庫裏面查詢到的一個對象,記住,託管狀態必須的和事物關聯上來

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

3、遊離狀態

當咱們使用EntityManager中的clear()方法的時候,它會把事物管理器中的全部實體變成遊離狀態,
處在遊離狀態的實體bean,咱們修改它的屬性它不會同步到數據庫中的

em.getTransaction().begin();
    em.clear(); // 把實體管理器中的全部實體都變成遊離狀態
    person.setName("老劉");
    em.merge(person);
    em.getTransaction().commit();
    em.close();
    factory.close();

4、關閉狀態

就是當咱們把Session關閉的時候,實體bean處於的狀態

JPA中使用EJQL語句

查詢

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();
    * 這個就至關於Hibernate中的uniqueResult();
    * 若是沒有查詢到數據的話會出錯的,全部咱們通常不這樣作
    * 咱們先獲得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分鐘內
    * 比方說這裏有人直接操做數據庫改了數據,咱們如何來獲得person呢
    * 有人說咱們能夠再執行一下find方法,這個事不能夠的,由於你再次
    * 執行find方法的時候EntityManager會中一級緩存中取得id是1的
    * Person數據,也就是說不能獲得最新的數據,那麼若是咱們想獲得最
    * 新的數據怎麼辦呢?JPA幫咱們作了一個刷新的方法em.refresh(person);
    * 經過它咱們就能夠獲得最新的數據了
    */
    em.refresh(person);
    System.out.println("----->>"+person.getName());
    em.close();
    factory.close();
}

JPA中實現表間關聯關係

一的多

@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設置級聯刷新
     * CascadeType.PERSIST設置級聯保存
     * CascadeType.MERGE設置級聯更新
     * CascadeType.REMOVE設置級聯刪除
     * CascadeType.ALL設置四種級聯
     * 這四種級聯都是對應的四種方法才能起做用PERSIST(),MERGE(),REMOVE(),REFRESH()
     * fetch=FetchType.LAZY支持LAzy加載,只有用到該屬性的時候纔會讀集合數據
     * 注意這時咱們必須保持EntityManager是開着的,默認是延遲加載
     * mappedBy="order"指定OrderItem類裏面的order這個屬性來維護關係
     */
    @OneToMany(cascade={CascadeType.ALL},
                    fetch=FetchType.LAZY,
                    mappedBy="order")
    private Set<OrderItem> items = new HashSet<OrderItem>();

    // get set //
}

爲了咱們添加Set集合方便咱們一般的狀況下咱們的寫一個添加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")指明關聯關係的子段名
     * 即定義外鍵的名稱
     *   
     * */
    @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
    @JoinColumn(name="order_id")
    private Order order;
    // get set //
}

測試

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();
}

這裏咱們並無保存orderIterm
可是咱們在數據庫中依然能看到orderIterm的數據
保存到數據庫裏面了,這裏就是設置級聯保存的緣故
CascadeType.PERSIST設置級聯保存

一對一

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;
    }
}

測試

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();
}

多對多

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"))
        建立中間表來維護兩個表的關係
     * joinColumns=@JoinColumn(name="student_id")定義維護端在中間表中的關聯字段
     * inverseJoinColumns=@JoinColumn(name="teacher_id")定義被維護端在中間表中的關聯字段
     * 
     */
    @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;
    }
}

測試

@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();
}

/**
* 創建學生跟老師的關係
*/
@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();
}

/**
* 解除學生跟老師的關係
*/
@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();
    /**
    * 刪除老師的時候必定的解除掉老師和學生的關係
    */
    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();
}

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

    /**
    * 這個爲何不須要解除關係呢,主要是由於學生這一段是維護端
    */
    Student student = em.find(Student.class, 1);
    em.remove(student);
    em.getTransaction().commit();
    em.close();
    factory.close();
}

聯合主鍵

咱們單獨寫一個類做爲主鍵,可是這個類的要知足3條規則

  1. 它必須實現Serializable接口
  2. 它必須重寫字段的hashCode()和equals()方法
  3. 要在類上代表@Embeddable,這個註解的意思就是讓它的屬性在AirLine也正常作屬性

那麼咱們在主體類裏面用這個主鍵類作爲id,在id上面加上@EmbeddedId代表就是用主鍵類的屬性做爲主鍵,並映射到數據庫表中

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;
    // 注意這裏必定的時間他HashCode()和equals()方法
}

測試

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 Boot

配置

使用步驟相似上面介紹,關鍵是要理解一對1、一對多、多對1、多對多的關聯關係

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

引入jpa

yml文件

# maven打包 clean compile package

spring:
    # 熱重啓
    devtools:
    restart:
      enabled: true
    # 數據源
    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

實體類映射數據表

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鏈接的針腳位
    // 單向一對一
    @OneToOne()
    @JoinColumn(name = "ledtype_id")
    private LEDType ledType;
}

使用jpa接口方法

LEDRepository.java

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

mvc中使用

@Autowired
private LEDPowerRepository ledPowerRepository;

個別使用方法能夠瀏覽其餘教程介紹增強一下

Spring For All 社區 Spring Data JPA 從入門到進階系列教程
SpringBoot實戰SpringDataJPA
使用 Spring Data JPA 簡化 JPA 開發

相關文章
相關標籤/搜索