1. 配置JPA环境

  • maven工程导入坐标: ```xml UTF-8 5.0.7.Final

junit junit 4.12 org.hibernate hibernate-entitymanager ${project.hibernate.version}

org.hibernate hibernate-c3p0 ${project.hibernate.version} mysql mysql-connector-java 5.1.6

log4j log4j 1.2.17

  1. - 配置 JPA 的核心配置文件
  2. - 位置:配置到类路径下的一个叫做 META-INF 的文件夹下
  3. - 命名:persistence.xml
  4. ```xml
  5. <?xml version="1.0" encoding="UTF-8"?>
  6. <persistence xmlns="http://java.sun.com/xml/ns/persistence"
  7. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  8. xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  9. version="2.0">
  10. <!--配置持久化单元
  11. name:持久化单元名称
  12. transaction-type:事务类型
  13. RESOURCE_LOCAL:本地事务管理 -- 所有表都在一个数据库中
  14. JTA:分布式事务管理 -- 不同表分散到不同数据库
  15. -->
  16. <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
  17. <!--配置 JPA 规范的服务提供商 -->
  18. <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
  19. <properties>
  20. <!-- 数据库驱动 -->
  21. <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
  22. <!-- 数据库地址 -->
  23. <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpaTest" />
  24. <!-- 数据库用户名 -->
  25. <property name="javax.persistence.jdbc.user" value="root" />
  26. <!-- 数据库密码 -->
  27. <property name="javax.persistence.jdbc.password" value="123456" />
  28. <!-- Hibernate特有的配置 -->
  29. <!-- 在 console 打印生成的sql -->
  30. <property name="hibernate.show_sql" value="true" />
  31. <!-- Pretty print the SQL in the log and console. -->
  32. <property name="hibernate.format_sql" value="true" />
  33. <!-- 自动创建数据库表 : hibernate.hbm2ddl.auto
  34. create : 程序运行时创建数据库表(如果有表,先删除表再创建)
  35. update :程序运行时创建数据库表(如果有表,不会创建表)
  36. none :不会创建表 -->
  37. <property name="hibernate.hbm2ddl.auto" value="create" />
  38. </properties>
  39. </persistence-unit>
  40. </persistence>
  • 创建客户的数据库表

    1. /*创建客户表*/
    2. CREATE TABLE cst_customer (
    3. cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
    4. cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
    5. cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源',
    6. cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业',
    7. cust_level varchar(32) DEFAULT NULL COMMENT '客户级别',
    8. cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址',
    9. cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话',
    10. PRIMARY KEY (`cust_id`)
    11. )
    12. ENGINE=InnoDB
    13. AUTO_INCREMENT=1
    14. DEFAULT CHARSET=utf8;
  • 创建客户实体类

    1. public class Customer implements Serializable {
    2. private Long custId;
    3. private String custName;
    4. private String custSource;
    5. private String custLevel;
    6. private String custIndustry;
    7. private String custPhone;
    8. private String custAddress;
    9. // getter setter and toString
    10. }
  • 配置实体类和表的映射关系:

    1. @Entity
    2. @Table(name = "cst_customer")
    3. public class Customer implements Serializable {....}
  • 配置字段和表的映射关系:

    • 配置主键

      1. @Id
      2. @GeneratedValue(strategy = GenerationType.AUTO)
      3. @Column(name = "cust_id")
      4. private Long custId;
    • 其他属性,直接使用@Column

      1. @Column(name = "cust_name")
      2. private String custName;
      3. @Column(name = "cust_source")
      4. private String custSource;
      5. @Column(name="cust_level")
      6. private String custLevel;
      7. @Column(name = "cust_industry")
      8. private String custIndustry;
      9. @Column(name="cust_phone")
      10. private String custPhone;
      11. @Column(name = "cust_address")
      12. private String custAddress;
  • 实现保存操作:

    1. public class JPATest {
    2. /**
    3. * 保存一个客户到数据库中
    4. */
    5. @Test
    6. public void saveCustomerTest(){
    7. //1.加载配置文件创建 EntityManagerFactory
    8. EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
    9. //2.根据实体管理器工厂 EntityManagerFactory,创建实体管理器 EntityManager
    10. EntityManager entityManager = factory.createEntityManager();
    11. //3.创建事务对象,开启事务
    12. EntityTransaction transaction = entityManager.getTransaction();//获取事务对象
    13. transaction.begin();//开启事务
    14. //4.增删改查操作: 保存一个客户到数据库中
    15. Customer customer = new Customer();
    16. customer.setCustName("IBM");
    17. customer.setCustAddress("Canada");
    18. customer.setCustIndustry("IT");
    19. //保存
    20. entityManager.persist(customer);
    21. //5.提交事务
    22. transaction.commit();
    23. //6.释放资源
    24. entityManager.close();
    25. factory.close();
    26. }
    27. }
  • 查看 Console Output: ```java Hibernate: drop table if exists cst_customer Hibernate: drop table if exists hibernate_sequence Hibernate: create table cst_customer (

    1. cust_id bigint not null,
    2. cust_address varchar(255),
    3. cust_industry varchar(255),
    4. cust_level varchar(255),
    5. cust_name varchar(255),
    6. cust_phone varchar(255),
    7. cust_source varchar(255),
    8. primary key (cust_id)

    ) Hibernate: create table hibernate_sequence (

    1. next_val bigint

    ) Hibernate: insert into hibernate_sequence values ( 1 ) Hibernate: select

    1. next_val as id_val

    from

    1. hibernate_sequence for update

Hibernate: update hibernate_sequence set next_val= ? where next_val=? Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, cust_id) values (?, ?, ?, ?, ?, ?, ?)

  1. <a name="xZs9k"></a>
  2. # 2. JPA的基本操作
  3. <a name="DhFHZ"></a>
  4. ## A. JPA 核心配置
  5. 1. *位置:配置到类路径下的一个叫做 META-INF 的文件夹下<br />*命名:persistence.xml
  6. 1. 在 persistence.xml 中配置数据库连接信息:
  7. ```xml
  8. <!-- 数据库驱动 -->
  9. <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
  10. <!-- 数据库地址 -->
  11. <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpaTest" />
  12. <!-- 数据库用户名 -->
  13. <property name="javax.persistence.jdbc.user" value="root" />
  14. <!-- 数据库密码 -->
  15. <property name="javax.persistence.jdbc.password" value="123456" />

c. 配置Hibernate的特定配置

  1. <!-- 在 console 打印生成的sql -->
  2. <property name="hibernate.show_sql" value="true" />
  3. <!-- Pretty print the SQL in the log and console. -->
  4. <property name="hibernate.format_sql" value="true" />
  5. <!-- 自动创建数据库表 : hibernate.hbm2ddl.auto
  6. create : 程序运行时创建数据库表(如果有表,先删除表再创建)
  7. update :程序运行时创建表(如果有表,不会创建表)
  8. none :不会创建表 -->
  9. <property name="hibernate.hbm2ddl.auto" value="update" />

B. JPA 注解

  • 实体类和表的映射:
    • @Entity - 指定当前类是实体类。
    • @Table - 指定实体类和表之间的对应关系。
      属性: name - 指定数据库表的名称
  • 主键字段的映射:
    • @Id - 声明主键的配置
    • @GeneratedValue - 配置主键的生成策略
      属性:strategy
      • GenerationType.IDENTITY :自增,mysql
        底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
      • GenerationType.SEQUENCE : 序列,oracle
        底层数据库必须支持序列
      • GenerationType.TABLE : jpa提供的一种机制,通过在数据库中建立一个 table hibernate_sequences, 来记录自增主键。

image.png

  1. - GenerationType.AUTO 由程序自动的帮助我们选择主键生成策略 ==> 一般会选择TABLE
  • 普通字段的映射:

    • @Column:配置属性和字段的映射关系
      属性:name - 数据库表中字段的名称

      C. JPA操作的步骤

    1. 加载配置文件, 创建 EntityMnagerFactory

      • Persisitence:静态方法(根据持久化单元名称创建实体管理器工厂)
        createEntityMnagerFactory()
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
      • EntityMnagerFactory内部维护的很多的内容:
        • 内部维护了数据库信息,
        • 维护了缓存信息
        • 维护了所有的实体管理器对象
        • 在创建EntityManagerFactory的过程中会根据配置创建数据库表
      • EntityManagerFactory的创建过程比较浪费资源:
        • 特点:线程安全的对象
          多个线程访问同一个EntityManagerFactory不会有线程安全问题
        • 如何解决创建浪费的问题?
          通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象 ```java /*
      • 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建一个EntityManager对象
      • 第二次方法getEntityManager方法:直接通过一个已经创建好的factory对象,创建EntityManager对象 */ public class JpaUtils { private static EntityManagerFactory factory; static { factory = Persistence.createEntityManagerFactory(“myJpa”); }

      public static EntityManager getEntityManager() { return factory.createEntityManager(); }

      public static void closeFactory(){ factory.close(); } } `` b. 创建实体管理器 EntityManager<br />EntityManager em = factory.createEntityManager();`
      EntityManager对象:实体类管理器:

      • getTransaction(): 创建事务对象
      • presist(): 保存
      • merge(): 更新
      • remove(): 删除
      • find/getRefrence(): 根据id查询

c. 创建事务对象,开启事务
EntityTransaction tx = em.getTransaction(); //获取事务对象
tx.begin();//开启事务
Transaction 对象 :

  1. - begin():开启事务
  2. - commit():提交事务
  3. - rollback():回滚

d. 增删改查操作
e. 提交事务:
tx.commit()
f. 释放资源:
em.close()
factory.close() //通过调用 JpaUtils 关闭

D. JPA相关操作的基本语法 - 增删改查

  • 查询:

    • T find(T, id)
    • getReference方法:延迟加载
      • 1.获取的对象是一个动态代理对象
      • 2.调用getReference方法不会立即发送sql语句查询数据库
      • 3.当调用查询结果对象的时候(System.out.println(customer);),才会发送查询的sql语句
        1. @Test
        2. public void findCustomer(){
        3. EntityManager entityManager = JpaUtils.getEntityManger();
        4. EntityTransaction tx = em.getTransaction();
        5. tx.begin();
        6. // select * from customer where id = xxx
        7. Customer customer = entityManager.find(Customer.class, 1L);
        8. //Customer customer = entityManager.getReference(Customer.class, 1L);
        9. tx.commit();
        10. entityManager.close();
        11. System.out.println(customer);
        12. }
  • 添加:entityManager.persist(obj)

    1. Customer customer = new Customer();
    2. customer.setCustName("传智播客");
    3. customer.setCustIndustry("教育");
    4. entityManager.persist(customer); //保存操作
  • 删除:entityManager.remove(obj)

    1. entityManager.remove(customer);
  • 更新:entityManager.merge(customer)

    1. customer.setCustIndustry("it教育");
    2. entityManager.merge(customer);

    E. JPQL查询

    JPQL 是 JPA 提供一种查询语言。

  • sql:查询的是表和表中的字段。

  • jpql:Java Persistence Query Language, 查询的是实体类和类中的属性, jpql和sql语句的语法相似。

写码思路:

  1. 创建query查询对象: String jpql = “from Customer where custName like ? “; Query query = em.createQuery(jpql);
  2. 对参数进行赋值: query.setParameter(1,”传智播客%”);
    1. 第一个参数:占位符的索引位置(从1开始)
    2. 第二个参数:取值
  3. 查询,并得到返回结果:

    1. List list = query.getResultList() : 直接将查询结果封装为list集合
    2. Object obj = query.getSingleResult() : 得到唯一的结果集

      1. 查询全部:

      ```java /**
    • 测试jqpl查询 */ public class JpqlTest {

      /**

      • 查询全部
      • jqpl:from cn.itcast.domain.Customer
      • sql:SELECT FROM cst_customer / @Test public void testFindAll() { //1.获取entityManager对象 EntityManager em = JpaUtils.getEntityManager(); //2.开启事务 EntityTransaction tx = em.getTransaction(); tx.begin(); //3.查询全部 String jpql = “from Customer “; Query query = em.createQuery(jpql);//创建Query查询对象,query对象才是执行jqpl的对象

        //发送查询,并封装结果集 List list = query.getResultList();

        for (Object obj : list) { System.out.print(obj); }

        //4.提交事务 tx.commit(); //5.释放资源 em.close(); } } ```

        2. 排序查询:倒序

        ```java /**

    • 排序查询: 倒序查询全部客户(根据id倒序)
    • sql:SELECT * FROM cst_customer ORDER BY cust_id DESC
    • jpql:from Customer order by custId desc */ @Test public void testOrders() { //1.获取entityManager对象 EntityManager em = JpaUtils.getEntityManager(); //2.开启事务 EntityTransaction tx = em.getTransaction(); tx.begin(); //3.查询全部 String jpql = “from Customer order by custId desc”; Query query = em.createQuery(jpql);//创建Query查询对象,query对象才是执行jqpl的对象

      //发送查询,并封装结果集 List list = query.getResultList();

      for (Object obj : list) { System.out.println(obj); }

      //4.提交事务 tx.commit(); //5.释放资源 em.close(); }
      ```

      3. 条件查询

      ```java /**

    • 条件查询
    • 案例:查询客户名称以‘传智播客’开头的客户
    • sql:SELECT * FROM cst_customer WHERE cust_name LIKE ?
    • jpql : from Customer where custName like ? */ @Test public void testCondition() { //1.获取entityManager对象 EntityManager em = JpaUtils.getEntityManager(); //2.开启事务 EntityTransaction tx = em.getTransaction(); tx.begin(); //3.查询全部 //i.根据jpql语句创建Query查询对象 String jpql = “from Customer where custName like ? “; Query query = em.createQuery(jpql); //ii.对参数赋值 — 占位符参数 //第一个参数:占位符的索引位置(从1开始),第二个参数:取值 query.setParameter(1,”传智播客%”);

      //iii.发送查询,并封装结果

      /**

      • getResultList : 直接将查询结果封装为list集合
      • getSingleResult : 得到唯一的结果集 */ List list = query.getResultList();

      for(Object obj : list) { System.out.println(obj); }

      //4.提交事务 tx.commit(); //5.释放资源 em.close(); } ```

      4. 统计查询

      如果使用SQL的统计函数,JPQL可以使用 select count(*) from ... ```java /**

    • 使用jpql查询,统计客户的总数
    • sql:SELECT COUNT(cust_id) FROM cst_customer
    • jpql:select count(custId) from Customer */ @Test public void testCount() { //1.获取entityManager对象 EntityManager em = JpaUtils.getEntityManager(); //2.开启事务 EntityTransaction tx = em.getTransaction(); tx.begin(); //3.查询全部 //i.根据jpql语句创建Query查询对象 String jpql = “select count(custId) from Customer”; Query query = em.createQuery(jpql); //ii.对参数赋值 //iii.发送查询,并封装结果

      /**

      • getResultList : 直接将查询结果封装为list集合
      • getSingleResult : 得到唯一的结果集 */ Object result = query.getSingleResult();

      System.out.println(result);

      //4.提交事务 tx.commit(); //5.释放资源 em.close(); } ```

      5. 分页查询

      ```java /**

    • 分页查询
    • sql:select * from cst_customer limit ?,? (如果只有?就意味着从 0 开始查找?条数据)
    • jqpl : from Customer */ @Test public void testPaged() { //1.获取entityManager对象 EntityManager em = JpaUtils.getEntityManager(); //2.开启事务 EntityTransaction tx = em.getTransaction(); tx.begin(); //3.查询全部 //i.根据jpql语句创建Query查询对象 String jpql = “from Customer”; Query query = em.createQuery(jpql); //ii.对参数赋值 — 分页参数 //起始索引 query.setFirstResult(0); //每页查询的条数 query.setMaxResults(2);

      //iii.发送查询,并封装结果 List list = query.getResultList();

      for(Object obj : list) { System.out.println(obj); }

      //4.提交事务 tx.commit(); //5.释放资源 em.close(); } ```