Spring Data JPA 学习笔记

说明

首先来说JPA是一个持久化规范,也就是说当我们用JPA的时候我们不需要去选面向Hibernate的api编程了,这样就大大降低了偶和度了
这里介绍spring配置版和springboot配置版(推荐)


spring配置版

引入

JPA是一种规范,那么它的编程有哪些要求呢?
引入下载的jar包导入lib文件夹,然后我们的在src下面加上一个META-INF目录在该文件夹下面加上一个persistence.xml文件,这个文件的规范写法

persistence.xml




    org.hibernate.ejb.HibernatePersistence
        
            
            
            
            
            
            
            
            
            
            
             
        
    

这个文件中的写话我们可以再hibernate的jpa实现包里面找到对应的例子,这里我是使用的是hibernate来实现JPA实现,上面的配置也都和heibernate差不太多值得注意的:


  1. Persistence-unit持久性化单元,我们可以随便命名但是在后面回到他的,通过它来找到相关的配置信息
  2. transaction-type 是指事物的类型,在通常情况下我们是本地的,但是当我们遇到两个数据库保存的时候 我们会到到JTA事物来控制事务,也就是二次提交

实体类来映射数据表

Persion.java

@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.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就相当于Hibernate中的SessionFactory,
    然而EntityManager就相当于Hibernate中的Session。
  2. 在JPA中我们获得它的方式是Persistence.createEntityManagerFactory("jun");
    jun是在persistence.xml中持久化单元名称
  3. EntityManager em = factory.createEntityManager();
    其中EntityManager中的几种操作数据库方法有
    Persist(),find(),getReference(),merge(),remove()这里我就列出几种常见的
  4. 这里说有的getReference()就相当于Hibernate中的Load() 支持Lazy加载,
    getReference它会查找一个代理对象,当访问它的属性的时候它才会向数据库操作
  5. 但是值得注意的这个必须确保Session是打开状态

实体bean的几种状态

一、新建状态

new一个实体的时候它所处于的状态

二、托管状态

这个状态非常重要,当一个实体bean是托管状态的时候我们修改它的属性值得话,即使我们没有用到hibernate中的相关方法操作数据库,她也会同步到数据库里的,托管就是从数据库里面查询到的一个对象,记住,托管状态必须的和事物关联上来

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

三、游离状态

当我们使用EntityManager中的clear()方法的时候,它会把事物管理器中的所有实体变成游离状态,
处在游离状态的实体bean,我们修改它的属性它不会同步到数据库中的

    em.getTransaction().begin();
    em.clear(); // 把实体管理器中的所有实体都变成游离状态
    person.setName("老刘");
    em.merge(person);
    em.getTransaction().commit();
    em.close();
    factory.close();

四、关闭状态

就是当我们把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 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 items = new HashSet();

    // 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 teachers = new HashSet();
    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 students = new HashSet();

    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配置版介绍基本一致,关键是要理解一对一、一对多、多对一、多对多的关联关系


推荐使用Spring Boot

配置Maven依赖


    org.springframework.boot
    spring-boot-starter-data-jpa

配置JPA

# 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连接的针脚位
}

通过接口类继承使用JPA内封装实现的方法

// LEDRepository.java
public interface LEDRepository extends JpaRepository { }

最后可以在Controller类中使用

@Autowired
private LEDPowerRepository ledPowerRepository;

个别使用方法可以浏览其他教程介绍加强一下

Spring For All 社区 Spring Data JPA 从入门到进阶系列教程
SpringBoot实战SpringDataJPA
使用 Spring Data JPA 简化 JPA 开发

你可能感兴趣的:(Spring Data JPA 学习笔记)