美文网首页
Spring Data JPA 学习笔记

Spring Data JPA 学习笔记

作者: TsMask | 来源:发表于2018-07-12 16:08 被阅读0次

    说明

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


    spring配置版

    引入

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

    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差不太多值得注意的:

    <persistence-unit name="jun" transaction-type="RESOURCE_LOCAL">
    
    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<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配置版介绍基本一致,关键是要理解一对一、一对多、多对一、多对多的关联关系


    推荐使用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
        # 数据源
        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<LED, Long> { }
    

    最后可以在Controller类中使用

    @Autowired
    private LEDPowerRepository ledPowerRepository;
    

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

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

    相关文章

      网友评论

          本文标题:Spring Data JPA 学习笔记

          本文链接:https://www.haomeiwen.com/subject/lahrpftx.html