一、ORM思想
1、原始JDBC操作数据库出现的问题
操作繁琐
占位符赋值麻烦
2、JDBC问题解决思路
- 操作繁琐 就将jdbc封装到工具类
- 占位符值麻烦
1. 建立实体类和表的关系 2. 建立实体类中属性和表中字段的关系
3、ORM思想
ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了ORM对象关系映射
简单的说:ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
主要目的: 操作实体类就相当于操作数据库表
建立两个映射关系:
实体类和表的映射关系 实体类中属性和表中字段的映射关系
当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。
实现ORM的框架:Mybatis,Hirbernate
二、实现ORM思想的框架Hirbernate介绍
Hibernate是一个开放源代码的对象关系映射框架
它对JDBC进行了非常轻量级的对象封装,
它将POJO(java实体类对象)与数据库表建立映射关系,是一个全自动的orm框架
hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
三、JPA规范
JPA的全称是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。
JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
JPA是一套规范,实现JPA规范,内部由接口和抽象类组成
JDBC规范和JPA规范比较
四、JPA的基本操作(CRUD)
1、案例:添加一个客户到数据库的客户表中。
客户:就是一家公司
2、搭建环境
(1)创建maven工程导入依赖
创建项目jpa_basic
导入依赖
由于JPA是sun公司制定的API规范,所以我们不需要导入额外的JPA相关的jar包,只需要导入JPA的提供商的jar包。我们选择Hibernate作为JPA的提供商,所以需要导入Hibernate的相关jar包。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <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>8.0.27</version> </dependency> </dependencies>
(2)需要配置jpa的核心配置文件
配置到类路径下的一个叫做META-INF的文件夹下
创建文件吗,命名:persistence.xml
导入约束 选择约束模板
选择persistence_2.xml版本
将模板的约束复制放入MEAT_INFO文件下的persistence.xml中
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> </persistence>
配置persistence-unit节点(持久化单元)
- name:持久化单元名称 - transaction-type:事务管理方式 Jta:分布式事务管理 Resource_Local:本地事务管理 - Provider:jpa的实现方式 - 数据库信息 - 可选配置:配置jpa实现方式的配置信息
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <!--1.配置persistence-unit节点(持久化单元) transaction-type:事务管理方式 Jta:分布式事务管理 Resource_Local:本地事务管理 --> <persistence-unit name="my_jpa" transaction-type="RESOURCE_LOCAL"> <!--jpa的实现方式--> <!--数据库信息--> <!--可选配置,配置jpa实现方的配置信息--> </persistence-unit> </persistence>
(3)编写客户数据库表(cust_customer)和实体类(customer)
顾客的数据库表
CREATE TABLE cust_customer ( cust_id BIGINT ( 32 ) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', cust_name VARCHAR ( 32 ) NOT NULL COMMENT '客户名称(公司名称)', cust_source VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户信息来源', cust_industry VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户所属行业', cust_level VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户级别', cust_address VARCHAR ( 128 ) DEFAULT NULL COMMENT '客户联系地址', cust_phone VARCHAR ( 64 ) DEFAULT NULL COMMENT '客户联系电话', PRIMARY KEY ( `cust_id` ) ) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
实体类(customer)并配置实体类和表,类中的属性和表字段成一一对应关系
/** * @author 王恒杰 * @date 2022/2/21 9:47 * @Description: */ @Entity //声明实体类 @Table(name = "cust_customer") //建立实体类和表的映射关系 public class Customer implements Serializable { @Id //声明私有属性为主键 @GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略 @Column(name = "cust_id") private Long custId; @Column(name = "cust_name") private String custName; @Column(name = "custSource") private String custSource; @Column(name = "cust_industry") private String custIndustry; @Column(name = "cust_level") private String custLevel; @Column(name="cust_Address") private String custAddress; @Column(name="cust_phone") private String custPhone; public Customer() { } public Customer(Long custId, String custName, String custSource, String custIndustry, String custLevel, String custAddress, String custPhone) { this.custId = custId; this.custName = custName; this.custSource = custSource; this.custIndustry = custIndustry; this.custLevel = custLevel; this.custAddress = custAddress; this.custPhone = custPhone; } public Long getCustId() { return custId; } public void setCustId(Long custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } @Override public String toString() { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custSource='" + custSource + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custLevel='" + custLevel + '\'' + ", custAddress='" + custAddress + '\'' + ", custPhone='" + custPhone + '\'' + '}'; } }
(5)配置JPA的核心配置文件
在java工程的src路径下创建一个名为META-INF的文件夹,在此文件夹下创建一个名为persistence.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <!--1.配置persistence-unit节点(持久化单元) transaction-type:事务管理方式 Jta:分布式事务管理 Resource_Local:本地事务管理 --> <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL"> <!--jpa的实现方式 配置JPA规范的服务提供商 --> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <!--数据库信息--> <properties> <!--数据库驱动--> <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> <!--数据库地址--> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/customer?serverTimezone=UTC"/> <!--数据库名--> <property name="javax.persistence.jdbc.user" value="root"/> <!--数据库密码--> <property name="javax.persistence.jdbc.password" value="root"/> <!--可选配置,配置jpa实现方的配置信息--> <!--配置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>
(6)测试类·实现添加操作
/** * @author 王恒杰 * @date 2022/2/21 11:32 * @Description: */ public class InsertCustomerTest{ public static void main(String[] args) { /** * 创建实体管理类工厂,借助Persistence的静态方法获取 * 其中传递的参数为持久化单元名称,需要jpa配置文件中指定 */ EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa"); //创建实体管理类 EntityManager em = factory.createEntityManager(); //获取事务对象 EntityTransaction tx = em.getTransaction(); //开启事务 tx.begin(); Customer c = new Customer(); c.setCustName("王恒杰"); c.setCustAddress("天津商业大学"); c.setCustPhone("15120308630"); //保存操作 em.persist(c); //提交事务 tx.commit(); //释放资源 em.close(); factory.close(); } }
五、JPA中的主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue(生成值)设定的.这里的@id和@GeneratedValue都是JPA的标准用法。
JPA提供的四种标准用法为TABLE,SEQUENCE(sequence),IDENTITY(identity),AUTO(auto)。
具体说明如下:
1、IDENTITY:主键由数据库自动生成(主要是自动增长型)
用法:
@Id //声明私有属性为主键 @GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略 @Column(name = "cust_id") private Long custId;
2、SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
用法:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq") @SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment") private Long custId; //@SequenceGenerator源码中的定义 @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface SequenceGenerator { //表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中 String name(); //属性表示生成策略用到的数据库序列名称。 String sequenceName() default ""; //表示主键初识值,默认为0 int initialValue() default 0; //表示每次主键值增加的大小,例如设置1,则表示每次插入新记录后自动加1,默认为50 int allocationSize() default 50; }
3、AUTO:主键由程序控制
用法:
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long custId;
4、TABLE:使用一个特定的数据库表格来保存主键
用法:
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen") @TableGenerator(name = "pk_gen", table="tb_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="PAYABLEMOENY_PK", allocationSize=1 ) private Long custId; //@TableGenerator的定义: @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface TableGenerator { //表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中 String name(); //表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。 String table() default ""; //catalog和schema具体指定表所在的目录名或是数据库名 String catalog() default ""; String schema() default ""; //属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值 String pkColumnName() default ""; //属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值 String valueColumnName() default ""; //属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为“CUSTOMER_PK”。 String pkColumnValue() default ""; //表示主键初识值,默认为0。 int initialValue() default 0; //表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。 int allocationSize() default 50; UniqueConstraint[] uniqueConstraints() default {}; } //这里应用表tb_generator,定义为 : CREATE TABLE tb_generator ( id NUMBER NOT NULL, gen_name VARCHAR2(255) NOT NULL, gen_value NUMBER NOT NULL, PRIMARY KEY(id) )
六、JPA的API介绍
1、 Persistence对象
Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。
@Test public void test(){ /** * 1、创建EntityManagerFactory */ String unitName="myJpa"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(unitName); }
2、EntityManagerFactory
EntityManagerFactory 接口主要用来创建 EntityManager 实例
/** * 2、创建实体管理类 * EntityManagerFactory 接口主要用来创建 EntityManager 实例 */ EntityManager entityManager = entityManagerFactory.createEntityManager();
注意: 由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory 即可
3、EntityManager
在 JPA 规范中, EntityManager是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager将其持久化后才会变成持久化对象。EntityManager对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
我们可以通过调用EntityManager的方法完成获取事务,以及持久化数据库的操作
方法说明:
GetTransaction : 获取事务对象 Persist : 保存操作 Merge : 更新操作 Remove : 删除操作 Find/GetReference : 根据id查询
实现代码
/** * 3、entityManager相关操作 * * 1.GetTransaction : 获取事务对象 * 2.Persist(坚持) : 保存操作 * 3.Merge (合并): 更新操作 * 4.Remove(删除) : 删除操作 * 5.Find/GetReference(查找/获取引用) : 根据id查询 */ //获取事务对象 EntityTransaction transaction = entityManager.getTransaction(); //开启事务 transaction.begin(); //提交事务· transaction.commit(); //回滚事务 transaction.rollback(); //添加操作 entityManager.persist(); //更新操作 entityManager.merge(); //删除操作 entityManager.remove(); //查询操作 entityManager.find();
4、EntityTransaction
在 JPA 规范中, EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单
//获取事务对象 EntityTransaction transaction = entityManager.getTransaction(); //开启事务 transaction.begin(); //提交事务· transaction.commit(); //回滚事务 transaction.rollback();
七、JPA的工具类 JpaUtil
1、工具类JpaUtil
/** * @author 王恒杰 * @date 2022/2/21 13:19 * @Description: */ public class JpaUtil { /** * Jpa的实体类管理工具:相当于Hirbernate的SessionFactory */ private static EntityManagerFactory em; //静态代码块赋值 static { //注意:该方法参数必须和persistence.xml中persistence-unit标签name属性取值一致 em = Persistence.createEntityManagerFactory("myJpa"); } /** * 使用管理器工厂生产一个管理器对象 * @return 管理器对象 */ public static EntityManager getEntityManager() { EntityManager entityManager = em.createEntityManager(); return entityManager; } }
2、使用工具类实现添加操作
/** * 使用工具类实现添加操作 */ @Test public void test() { //1.获取事务对象 EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); //2.开启事务 transaction.begin(); Customer customer = new Customer(null, "天津以琳快讯科技有限公司", "58同城", "IT行业", "1级别", "古城创业大厦", "123456"); //3.添加操作 entityManager.persist(customer); //4.提交事务· transaction.commit(); //5.关闭资源 entityManager.close(); }
底层实现的Sql语句
insert into cust_customer (cust_Address, cust_industry, cust_level, cust_name, cust_phone, custSource) values (?, ?, ?, ?, ?, ?)
八、使用JPA完成增删改查操作
1、添加数据
/** * @author 王恒杰 * @date 2022/2/21 10:13 * @Description: */ public class JpaTest { /** * 使用工具类实现添加操作 */ @Test public void InsertCustomer() { //1.获取事务对象 EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); //2.开启事务 transaction.begin(); Customer customer = new Customer(null, "天津商业大学", "今日校园", "教育", "、特殊级别", "天津北辰天穆镇", "8888888"); //3.添加操作 entityManager.persist(customer); //4.提交事务· transaction.commit(); //5.关闭资源 entityManager.close(); } }
底层实现Sql语句
insert into cust_customer (cust_Address, cust_industry, cust_level, cust_name, cust_phone, cust_Source) values (?, ?, ?, ?, ?, ?)
2、修改数据(先查询出来再修改)
/** * 修改数据 */ @Test public void update() { //1. 获取实体类管理器 EntityManager entityManager = JpaUtil.getEntityManager(); //获取事务 EntityTransaction transaction = entityManager.getTransaction(); //开启事务 transaction.begin(); //查询出来需要修改的数据 id=5 Customer customer = entityManager.find(Customer.class, 5L); // 修改id=5 的顾客 地址为贵州省遵义市播州区 customer.setCustAddress("贵州省遵义市播州区"); //修改数据 Customer merge = entityManager.merge(customer); System.out.println("数据修改成功后数据" + merge); // 提交事务 transaction.commit(); //关闭链接 entityManager.close(); }
底层实现的SQL语句
update cust_customer set cust_Address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_Source=? where cust_id=?
3、查询数据
(1)根据id查询,使用立即加载的策略
@Test public void selectCustomerById(){ EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); //查询id=5的顾客 Customer customer = entityManager.find(Customer.class, 5l); System.out.println(customer); }
(2)查询实体的缓存问题
@Test public void selectCustomerById() { EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); //查询id=5的顾客 Customer customer1 = entityManager.find(Customer.class, 5l); Customer customer2 = entityManager.find(Customer.class, 5l); // 输出结果是true,EntityManager也有缓存 System.out.println(customer1==customer2); }
(3)延迟加载策略的方法:Reference
@Test public void selectCustomerByIdAndReference() { EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); //查询id=5的顾客 Customer customer1 = entityManager.getReference(Customer.class,5L); // 输出结果是true,EntityManager也有缓存 System.out.println(customer1); }
4、删除数据
/** * 删除数据 */ @Test public void deleteCustomer() { EntityManager entityManager = JpaUtil.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); //查询id=5的顾客 Customer customer= entityManager.getReference(Customer.class, 5L); //删除 entityManager.remove(customer); //提交事务 transaction.commit(); //关闭资源 entityManager.close(); }
- 实现Sql语句
delete from cust_customer where cust_id=?