JPA的使用

1. 简介

Jpa 是一套ORM 的规范
hibernate 不就是一个 ORM 框架也提供了对于 JPA 的实现

JPA(Java Persistence API):java 持久化 API

2. 常用注解

2.1 @Entity

标注当前类为实体类,将映射到指定的数据库表中

@Entity
public class Users {
		
}

2.2 @Table

一般与 @Entity 注解一起使用,如果数据库表名和类名一致时不使用 @Table 注解也是可以的,
否则需要使用 @Table 注解来指定表名

@Entity
@Table(name="t_users")
public class Users {
		
}

2.3 @Id 、@GeneratedValue、@SequenceGenerator、@Column
2.3.1 @Id

用于将实体类的属性映射为主键

2.3.2 @GeneratedValue

指定主键生成策略

package javax.persistence;
/**
 * 策略类型
 */
public enum GenerationType {
  /**
   * 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
   */
  TABLE,
  /**
   * 通过序列产生主键,通过 @SequenceGenerator 注解指定序列名
   * MySql 不支持这种方式
   * Oracle 支持
   */
  SEQUENCE,
  /**
   * 采用数据库 ID自增长的方式来自增主键字段
   * Oracle 不支持这种方式;
   */
  IDENTITY,
  /**
   * 缺省值,JPA 根据数据库自动选择
   */
  AUTO;

  private GenerationType() {
  }
}
2.3.3 @SequenceGenerator
2.3.4 @Column

当实体类属性名和数据库列名不一致时必须要使用此注解

@Entity
@Table(name="t_users")
public class Users {

  @Id
  @Column(name = "user_id")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
  @SequenceGenerator(name = "user_seq", sequenceName = "user_seq")
  private Long userId;
		
}
2.4 @Transient

表示当前属性无需映射到数据库中

2.5 @Temproal

主要针对 Date 类型的属性使用,可以通过该注解指定时间的精度

@Entity
@Table(name="t_users")
public class Users {

  @Temporal(TemporalType.DATE) 
  private Date time1;
  
  @Temporal(TemporalType.TIME)
  private Date time2;
  
  @Temporal(TemporalType.TIMESTAMP)
  private Date time3;
}

3. EntityManagerFactory

类似与 hibernate 的 SessionFactory

4. EntityManager

实体的四种状态
  • 新建状态: 新创建还未拥有持久性主键
  • 持久化状态: 已经拥有持久性主键并和持久化建立了上下文关系
  • 游离状态: 拥有持久性主键,但没有和持久化建立上下文关系
  • 删除状态: 拥有持久性主键,并且和持久化建立了上下文关系,但是从数据库中删除了
4.1 find(Class entityClass, Object primaryKey)

类似于 hibernate 中 session 的 get()

  • find 如果没有查询到会返回 null
4.2 getReference(Class entityClass, Object primaryKey)

类似与 hibernate 中 session 的 load()
只有当真正获取对象中的属性时,才会去执行查询的 sql 语句,getReference() 只是返回了一个代理对象

  • getReference 如果没有查询到不会返回 null , 会抛出 EntityNotFoundException

注意:使用此方法可能出现懒加载异常的情况,也就是我们还没有去获取实体类中的属性值,结果 EntityManager 就已经被关闭了

4.3 persist

类似与 hibernate 中 session 的 save()

注意:执行方法时传入的对象不能为主键设置值会抛出异常

4.4 remove

类似与 hibernate 中 session 的 delete()

注意:该方法只能删除持久化对象,而不能删除游离状态的对象(hibernate 可以)

	/**
	 * 删除游离态(失败)
	 */
	public void testRemove(){
		Users user = new Users();
		Users.setUserId(1);
		entityManager.remove(customer);
	}
	
	/**
	 * 删除持久化状态(成功)
	 */
	public void testRemove(){
		Users user = entityManager.find(Users.class, 1);
		entityManager.remove(user);
	}
4.4 merge(T entity)

类似与 hibernate 中 session 的 saveOrUpdate()

	// 新建状态
	public void testMerge(){
		Users user= new Users();
		// 省略一系列的set
		// user.set.....
		Users newUser = entityManager.merge(user);
		// user.getUserId() == null  ==> true
		// newUser.getUserId() == null ==> false
	}

在这里插入图片描述

4.5 flush()

类似与 hibernate 中 session 的 flush()

将上下文中所有未保存的实体保存到数据库中

4.6 refresh()

类似与 hibernate 中 session 的 refresh()

刷新所有实体的属性值

5. EntityTransaction

EntityManager.getTransaction()

5.1 begin
5.2 commit
5.3 rollback

6. 映射关系

6.1 单向一对多

以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
对于一对多关系的 insert,无论是先插入多的一方还是一的一方都会产生额外的 update 语句,因为多的一端在 insert 时不会插入外键的列

/**
 * 用户和订单是一对多的关系
 */
@Entity
@Table(name="t_users")
public class User {
	// fetchType默认为lazy懒加载
	@OneToMany
	// user_id为外键列名(在order表中是根据 user_id 去查询的)
	// 使用 set 集合
	@JoinColumn(name="user_id")
	private Set<Orders> orders;
}

6.2 单向多对一

以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
对于多对一关系的 insert,最好先保存一的一端然后在保存多的一端。
如果先保存多的一端再保存一的一端,为了维护外键的关系,需要对多的一端进行额外的update的操作

/**
 * 订单和用户是多对一的关系
 */
@Entity
@Table(name="t_order")
public class Order {
	// lazy为懒加载,默认为eager立即查询
	@ManyToOne(fetch=FetchType.Lazy)
	// @JoinColumn标注字段是一个类,userId为该类的主键
	@JoinColumn(name="user_id")
	private Users user;
}

6.3 双向多对一

以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
双向多对一就是以上两个的结合,同时使用 @OneToMany 和 @ManyToOne

/**
 * 用户和订单是一对多的关系
 */
@Entity
@Table(name="t_users")
public class User {
	// 如果两侧都要描述关联关系的话,维护关联关系的任务要交给多的一方
	// 使用 @OneToMany 了 mappedBy 的代表不维护关联关系,也就是不会产生额外的update语句
	// @OneToMany 和 @JoinColumn 不能同时使用会报错
	@OneToMany(mappedBy="user")
	private Set<Orders> orders;
}

/**
 * 订单和用户是多对一的关系
 */
@Entity
@Table(name="t_orders")
public class Order {
	// lazy为懒加载,默认为eager立即查询
	@ManyToOne(fetch=FetchType.Lazy)
	// @JoinColumn标注字段是一个类,userId为该类的主键
	@JoinColumn(name="user_id")
	private Users user;
}

在这里插入图片描述

6.4 双向一对一

以学校和校长之间的关系为例,一个学校只有一个校长,一个校长也只属于一个学校
一方使用 @OneToMany + @JoinColumn,另一方使用 @OneToOne(mappedBy=“xx”)
具体由哪一方维护关联关系都可以,这里我们以学校一端维护关联关系为例
保存时先保存不维护关联关系的一方(也就是使用@OneToOne(mappedBy=“xx”)的一方),否则会产生额外的 update 语句

/**
 * 学校
 */
@Entity
@Table(name="t_school")
public class School {
	// 默认为eager立即查询
	@OneToOne
	// 添加唯一约束
	@JoinColumn(name="school_master_id", unique = true)
	private SchoolMaster schoolMaster;
}

/**
 * 校长
 */
@Entity
@Table(name="t_school_master")
public class SchoolMaster {
	// 不维护关联关系要使用 mappedBy
	@OneToOne(mappedBy="schoolMaster")
	private School school;
}
6.5 双向多对多

以学生和课程之间的关系为例,一个学生可以选多门课,一个课程也有多个学生,多对多需要一个中间表,也就是选课表
维护关联关系的一方需要使用 @JoinTable
关联关系也是只有一方维护即可,这里我们由学生表进行维护

/**
 * 学生
 */
@Entity
@Table(name="t_student")
public class Student {
	@GeneratedValue
	@Id
	private Long student_id;
	
	// 要使用 set 集合接收
	// 默认为lazy懒加载
	@ManyToMany
	// name 为中间表的表名
	@JoinTable(name="t_student_choose_course",
			// name 为与中间表与当前表所关联的字段的名称,referencedColumnName 为当前表中与中间表关联的字段的名称
			joinColumns={@JoinColumn(name="student_id", referencedColumnName="student_id")},
			// name 为与中间表与多对多另一方表所关联的字段的名称,referencedColumnName 为多对多另一方与中间表关联的字段的名称
			inverseJoinColumns={@JoinColumn(name="course_id", referencedColumnName="course_id")})
	private Set<Course> courses;
}

/**
 * 课程
 */
@Entity
@Table(name="t_course")
public class Course {
	@GeneratedValue
	@Id
	private Long course_id;
	
	// 要使用 set 集合接收
	// 默认为lazy懒加载
	@ManyToMany(mappedBy="courses")
	private Set<Student> students;
}

7. 二级缓存

开启了二级缓存之后,缓存是可以跨越 EntityManager 的,
默认是一级缓存也就是在一个 EntityManager 中是有缓存的
二级缓存可以实现,关闭了 EntityManager 之后缓存不会被清除
使用 @Cacheable(true) 开启二级缓存

8. JPQL

8.1 查询接口
8.1.1 createQuery
	public void testCreateQuery(){
		// 这里我们使用了一个 new Student,因为我们是查询 Student 中的部分属性,如果不适用 new Student 查询返回的结果就不是 Student 类型而是一个 Object[] 类型的 List
		// 也可以在实体类中创建对应的构造器,然后使用如下这种 new Student 的方式,来把返回结果封装为Student 对象
		String jpql = "SELECT new Student(s.name, s.age) FROM t_student s WHERE s.student_id > ?";
		// setParameter 时下标是从1开始的
		List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();
	}
8.1.2 createNamedQuery

需要在类上使用 @NamedQuery 注解,事先声明 sql 语句

@NamedQuery(name="testNamedQuery", query="select * from t_student WHERE student_id = ?")
@Entity
@Table(name="t_student")
public class Student {
	@GeneratedValue
	@Id
	private Long student_id;
	
	@Column
	private String name;
	
	@Column
	private int age;
}
	public void testCreateNamedQuery(){
		Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3);
		Student student = (Student) query.getSingleResult();
	}
8.1.3 createNativeQuery
	public void testCreateNativeQuery(){
		// 本地sql的意思是只能在数据库中执行的sql语句
		String sql = "SELECT age FROM t_student WHERE student_id = ?";
		Query query = entityManager.createNativeQuery(sql).setParameter(1, 18);
		Object result = query.getSingleResult();
	}
8.2 关联查询

存在一对多关系时,当我们查询一的一端时,默认多的一端是懒加载。此时我们如果想要一次性查询出所有的数据就需要使用关联查询

注意: 下面 sql 中的重点就是要加上 fetch u.orders,表示要查询出用户所关联的所有订单

	public void testLeftOuterJoinFetch(){
		String jpql = "FROM t_users u LEFT OUTER JOIN FETCH u.orders WHERE u.id = ?";
		
		Users user = (Users) entityManager.createQuery(jpql).setParameter(1, 123).getSingleResult();
	}
Java使用JPAJava Persistence API)可以方便地进行对象关系映射(ORM)操作。JPA提供了一些缓存机制来提升性能和减少数据库访问次数。 JPA的缓存主要分为两种:一级缓存(实体管理器缓存)和二级缓存(应用级缓存)。 一级缓存是JPA实体管理器的默认缓存,它保存在内存中,用于存储已经查询过或持久化过的实体对象。当通过JPA查询实体时,JPA会首先检查一级缓存中是否存在相应的实体对象,如果存在,则直接返回缓存中的对象,减少数据库查询。一级缓存是默认开启的,无需额外配置。 二级缓存是一个可选的缓存层,用于跨会话和跨实体管理器的缓存。它可以将实体对象保存在共享的缓存中,避免重复查询数据库。二级缓存可以提高性能,但需要额外配置和管理。常见的二级缓存实现包括Ehcache、Redis和Hazelcast等。 要使用二级缓存,需要在持久化单元配置文件(persistence.xml)中进行相应配置,并在实体类上使用注解进行缓存配置。例如,使用Ehcache作为二级缓存的配置示例: ```xml <persistence-unit name="myPersistenceUnit" transaction-type="JTA"> <properties> <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/> ... </properties> </persistence-unit> ``` ```java @Entity @Cacheable(true) @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class User { ... } ``` 在配置好二级缓存后,JPA会自动将查询结果缓存起来,下次查询相同的实体时,会直接从缓存中获取,而不是再次查询数据库。 需要注意的是,使用缓存时需要考虑数据的一致性和更新问题。在更新或删除实体时,需要手动清除或更新缓存,以避免数据不一致的情况发生。 以上是关于Java JPA使用缓存的简要介绍,希望能对你有所帮助!如有更多疑问,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值