zoukankan      html  css  js  c++  java
  • 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实现。

  • 相关阅读:
    npm install、npm install --save与npm install --save-dev、npm install -g区别
    react的几种loader
    React的Sass配置
    react中使用css的7种方式
    linux安装字体
    反向代理和正向代理的解释
    开源Odoo ERP13.2版本模块功能
    Git+码云构建自己的共享仓库
    .Net Core的依赖注入
    libpcap使用(1)
  • 原文地址:https://www.cnblogs.com/wushenjiang/p/13196845.html
Copyright © 2011-2022 走看看