SpringDataJpa学习(1)——Jpa学习

写在前面

众所周知,市面上有两大十分流行的持久层框架——Hibernate和Mybatis,之前的SSM框架就是使用的Mybatis框架,就不再过多的说了。至于Hibernate框架,他是jpa规范的实现。所谓的jpa规范,拿百度百科的解释来说,就是java官方公司规定的一套规范。Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。(百度百科)
而SpringDataJpa是Spring公司对jpa规范的又一层深度封装。因此在学习他之前有必要学习一下Jpa的使用。

jpa的核心思想——ORM

按照英文意思来说,(Object-Relational Mapping),表示对象关系映射。我们一直在使用的java语言就是一套面向对象的语言,通过orm,我们就可以把对象映射到关系型数据库。即先有实体类后有表。操作对象就可以直接操作我们的数据库表了。这种思想的出现也很好理解,很多SQL语句都是重复的,通过orm我们就可以大大地减少重复性的代码。

jpa的使用

准备工作

我们使用maven来搭建工程,导入相关的坐标:

   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.hibernate.version>5.0.7.Final</project.hibernate.version>
    </properties>

    <dependencies>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- hibernate对jpa的支持包 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- log日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- Mysql and MariaDB -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>

之后我们编写与实体类:

public class Customer implements Serializable {
    
	private Long custId;
	private String custName;
	private String custSource;
	private String custIndustry;
	private String custLevel;
	private String custAddress;
	private String custPhone;
}

同时生成对应的get,set方法和toString方法方便我们的使用。

编写映射配置

接下来为了实现操作对象就可以操作数据库,我们要配置一下映射。

/**
 * 1 实体类和表的映射关系 2 实体类中属性和表中字段的映射关系
 * @author wushen
 * @Entity:声明实体类
 * @Table: 配置实体类和表的映射关系 name:配置数据库表的名称
 */
@Entity
@Table(name = "cst_customer")
public class Custom {
    /**
     * 客户的主键
     * @Id:声明主键的配置
     * @GeneratedValue:配置主键的生成策略
     *      GenerationType.IDENTITY:自增(底层数据库必须支持自动增长) mysql
     *      GenerationType.SEQUENCE:序列(底层数据库必须支持序列) oracle
     *      GenerationType.TABLE:jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
     *      GenerationType.AUTO:由程序自动帮助我们选择主键生成策略
     * @Colunm:配置属性和字段的映射关系 name:数据库表中字段的名称
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;
    /**
     * 客户名称
     */
    @Column(name = "cust_name")
    private String custName;
    /**
     * 客户来源
     */
    @Column(name = "cust_source")
    private String custSource;
    /**
     *
     * 客户级别
     */
    @Column(name = "cust_level")
    private String custLevel;
    /**
     * 客户所属来源
     */
    @Column(name = "cust_industry")
    private String custIndustry;
    /**
     * 客户的联系方式
     */
    @Column(name = "cust_phone")
    private String custPhone;
    /**
     * 客户地址
     */
    @Column(name = "cust_address")
    private String custAddress;

其中,@Entity注解表示该类是一个实体类,@Table用来配置该实体类所对应的表名称。@Id表示该字段是一个主键,@GeneratedValue用来标注主键的生成策略,@Column用来标识该对应数据库中的哪个字段。这些配置都不是很难,详细可以查看代码中的注释。

配置JPA的核心配置文件

对于JPA的配置文件,我们需要在resources下新建一个文件夹META-INF(固定),新建一个配置文件persistence.xml(名字固定) 在里面写入相应的配置:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!-- 需要配置persistence-unit 节点
        持久化单元:
            name:持久化单元名称
            transaction-type:事务管理的方式
                JTA:分布式事务管理
                RESOURCE_LOCAL:本地事务管理
    -->
    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <!-- jpa的实现方式 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!-- 可选配置: 配置jpa实现方的配置信息 -->
        <properties>
            <!-- 数据库信息
                用户名:javax.persistence.jdbc.user
                密码:javax.persistence.jdbc.password
                驱动:javax.persistence.jdbc.driver
                数据库地址:javax.persistence.jdbc.url
            -->
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="abc456"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpatest?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true"/>
            <!-- 配置jpa实现方(hibernate)的配置信息
                显示sql :false|true
                自动创建数据库表:hibernate.hbm2ddl.auto
                    create :程序运行时创建数据库表(如果有表,先删除表再创建)
                    update: 程序运行时创建表(如果有表,不会创建表)
                    none : 不会创建表
            -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

具体的解释已经写在了注释上,请仔细看代码。

测试

在配置完了之后,我们就可以编写测试类来测试看看JPA了,如下代码:

  @Test
    /**
     * jpa的操作步骤:
     *  1.加载配置文件,创建工厂(实体管理类工厂)对象
     *  2.通过实体管理类工厂获取实体管理器
     *  3.获取事务对象,开启事务
     *  4.完成增删改查操作
     *  5.提交事务(回滚事务)
     *  6.释放资源
     */
    public void testSave(){
        // 1.加载配置文件,创建工厂(实体管理类工厂)对象
//        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
        // 2.通过实体管理类工厂获取实体管理器
//        EntityManager manager = factory.createEntityManager();
        EntityManager manager = JpaUtils.getEntityManager();
        // 3.获取事务对象,开启事务
        EntityTransaction tx = manager.getTransaction();
        // 开启事务
        tx.begin();
        // 4.保存一个客户到数据库中
        Custom custom = new Custom();
        custom.setCustName("武神酱");
        custom.setCustIndustry("IT黑马");
        // 保存
        manager.persist(custom);
        // 5.提交事务(回滚事务)
        tx.commit();
        // 6.释放资源
        manager.close();
        // factory.close();
    }

关于这里的工具类,下面会说到。
这里可以看到我们全程没有写一行Sql语句,都是由框架自动替我们生成的。这就是Jpa规范的优点。其中这里用到了persist方法来保存,其他的例如remove(删除),merge(更新)的代码都大同小异,这里就不再赘述了。剩下的我们再重点看看两个查询方法:find和getReference:

  @Test
    /**
     * find方法根据id查询客户
     *  1. 查询的对象就是当前客户对象本身
     *  2. 在调用find方法的时候,就会发送sql语句查询数据库
     * 立即加载
     *
     */
    public void testFind(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.增删改查 -- 根据id查询客户
        /**
         * find: 根据id查询数据
         *      class:查询的结果需要包装的实体类类型的字节码
         *      id:查询的主键的取值
         */
        Custom custom = manager.find(Custom.class, 1L);
        //System.out.println(custom);
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }
    @Test
    /**
     * getReference方法根据id查询
     *      1. 获取的对象是一个动态代理对象
     *      2. 调用getReference方法不会立即发送sql语句查询数据库
     *              当调用查询结果对象的时候,才会发送查询的sql语句,什么时候需要,什么时候发送sql语句查询数据库
     * 延迟加载(懒加载)
     *       得到的是一个动态代理对象
     *       什么时候用,什么时候才会查询
     */
    public void testReference(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.增删改查 -- 根据id查询客户
        /**
         * getReference: 根据id查询数据
         *      class:查询的结果需要包装的实体类类型的字节码
         *      id:查询的主键的取值
         */
        Custom custom = manager.getReference(Custom.class, 1L);
        System.out.println(custom);
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

可以看到,这两个查询语句类似,但getReference使用了懒加载(延迟加载)的方式,即什么时候需要什么时候再发送sql语句查询数据库,而find方法则用了立即加载的方式,即立刻发送sql语句。延迟加载的好处其实就是减轻压力,很多时候查询一堆内容是十分耗费资源的,而有时候只是查询到却没有使用,是没有必要的。延迟加载便是为了解决此类问题。

EntityManagerFactory和EntityManager

EntityManagerFactory主要是用来新建EntityManager实例的。但EntityManagerFactory的创建十分耗费资源,且是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory对象不会出现线程安全问题)我们可以自己写一个工具类:

public class JpaUtils {
    /**
     * 程序第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建EntityManager对象
     * 第二次访问的时候,直接通过一个已经创建好的factory对象,创建EntityManager对象
     */
    private  static EntityManagerFactory factory;
    static {
        // 1.加载配置文件,创建entityManagerFactory
        factory = Persistence.createEntityManagerFactory("myJpa");
    }

    /**
     * 获取EntityManagerFactory对象
     */
    public static EntityManager getEntityManager(){
        return factory.createEntityManager();
    }
}

jpql使用

jpql即java持久化查询语言,是一种以面向对象表达式语言的表达式,这种语言完全面向对象,且特征与原生sql很类似。
下面我们就来测试一下:

   /**
     * 查询全部
     *      jpql:from com.liuge.domain.Custom
     *      sql:select * from cst_customer
     */
    @Test
    public void testFindAll(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.查询全部
        String jpql = "from Custom";
        // 创建Query查询对象,query对象才是执行jpql的对象
        Query query = manager.createQuery(jpql);
        // 发送查询,并封装结果集
        List list = query.getResultList();
        for (Object o : list) {
            System.out.println(o);
        }
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

    /**
     * 倒序查询全部客户(id)
     *      sql:selct * from cst_customer order by cust_id desc
     *      jpql: from Custom order by custId desc
     *
     *      1.创建query查询对象
     *      2.对参数进行赋值
     *      3.查询并得到返回结果
     */
    @Test
    public void testOrder(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.查询全部
        String jpql = "from Custom order by custId desc";
        // 创建Query查询对象,query对象才是执行jpql的对象
        Query query = manager.createQuery(jpql);
        // 发送查询,并封装结果集
        List list = query.getResultList();
        for (Object o : list) {
            System.out.println(o);
        }
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

    /**
     * 使用jpql查询,统计客户的总数
     *      sql:select count(cust_id) from cst_customer;
     *      jpql:select count(custId) from Custom
     */
    @Test
    public void testCount(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.查询全部
        String jpql = "select count(custId) from Custom";
        // 1. 创建Query查询对象,query对象才是执行jpql的对象
        Query query = manager.createQuery(jpql);
        // 2. 对参数进行赋值
        // 3. 查询并得到返回结果
        Object result = query.getSingleResult();
        System.out.println(result);
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

    /**
     * 分页查询
     *      sql:select * from cst_customer limit ?,?
     *      jpql: from Custom
     */
    @Test
    public void testPaged(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.查询全部
        String jpql = "from Custom";
        // 1. 创建Query查询对象,query对象才是执行jpql的对象
        Query query = manager.createQuery(jpql);
        // 2. 对参数进行赋值 --- 分页参数
        // a) 起始索引 b) 每页查询的条数
        query.setFirstResult(0);
        query.setMaxResults(2);
        // 3. 查询并得到返回结果
        List list = query.getResultList();
        for (Object o : list) {
            System.out.println(o);
        }
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

    /**
     * 条件查询
     *      查询客户名称以"我"开头的客户
     *      sql:select * from cst_customer where cust_name like ?
     *      jpql:from Custom where custName like ?
     */
    @Test
    public void testCondition(){
        // 1.通过工具类获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        // 2.开启事务
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        // 3.查询全部
        String jpql = "from Custom where custName like ?";
        // 1. 创建Query查询对象,query对象才是执行jpql的对象
        Query query = manager.createQuery(jpql);
        // 2. 对参数进行赋值 --- 占位符参数
        // a)索引位置:从1开始 2):取值
        query.setParameter(1,"我%");
        // 3. 查询并得到返回结果
        List list = query.getResultList();
        for (Object o : list) {
            System.out.println(o);
        }
        // 4.提交事务
        tx.commit();
        // 5.释放资源
        manager.close();
    }

具体的解释都在代码中的注释上了。

总结

一套使用下来可以发现,jpa的使用除了繁琐的配置外,在使用时是十分方便的,基本不用写sql语句就可以实现基本的CRUD,而且复杂的CRUD也可以通过jpql实现。

posted @ 2020-06-26 23:35  武神酱丶  阅读(359)  评论(0编辑  收藏  举报