说明
首先来说JPA是一个持久化规范,也就是说当我们用JPA的时候我们不需要去选面向Hibernate的api编程了,这样就大大降低了偶和度了
这里介绍spring配置版和springboot配置版(推荐)。
spring配置版
引入
JPA是一种规范,那么它的编程有哪些要求呢?
引入下载的jar包导入lib文件夹,然后我们的在src下面加上一个META-INF目录在该文件夹下面加上一个persistence.xml文件,这个文件的规范写法
persistence.xml
org.hibernate.ejb.HibernatePersistence
这个文件中的写话我们可以再hibernate的jpa实现包里面找到对应的例子,这里我是使用的是hibernate来实现JPA实现,上面的配置也都和heibernate差不太多值得注意的:
- Persistence-unit持久性化单元,我们可以随便命名但是在后面回到他的,通过它来找到相关的配置信息
- transaction-type 是指事物的类型,在通常情况下我们是本地的,但是当我们遇到两个数据库保存的时候 我们会到到JTA事物来控制事务,也就是二次提交
实体类来映射数据表
Persion.java
@Entity //就是告诉JPA我是一个实体bean
@Table(name="t_person")//作为修改表名,默认情况是类名
public class Person {
/**
* GenerationType.AUTO它会根据数据库方言来选择正确的生成策略
* 默认情况下就是AUTO
*/
@Id//映射主键
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
// 映射一列到数据库中 length是指字段的长度
// nullable是指是否为空,默认情况是空
// 当我们不想让类字段名与数据库名一样的时候用到的
@Column(length=10,nullable=false,name="personname")
private String name;
// 映射日期类型TemporalType有好几种映射方式
// 我们可以根据自己的需求选择正确的映射方式
@Temporal(TemporalType.DATE)
@Column(nullable=false)
private Date birthday;
// 用枚举类型来映射性别EnumType有好几种映射方式
// 这里使用的是枚举的String类型,我们也一个选择枚举的索引
@Enumerated(EnumType.STRING)
@Column(nullable=false,length=5)
private Sex sex = Sex.MEN;
// 对应大文本和图片就是二进制
@Lob
private String info;
// 支持延迟加载(减少内存的消耗)
@Lob @Basic(fetch=FetchType.LAZY)
private Byte[] image;
// 不想这个字段与数据库表映射
@Transient
private String imagepath;
// 版本标识 防止脏读
@Version
private Integer version;
// get set //
}
利用jpa来操作数据库
Test.java
@Test
public void testSave() {
/**
* jun是在persistence.xml中持久化单元名称
*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
//--->>SessionFactory-->>session-->>begin
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();//开启事物
Person person = new Person("刘文军");
person.setBirthday(new Date());
em.persist(person);
System.out.println("----->>"+person.getSex().getName());
em.getTransaction().commit();
em.close();
factory.close();
}
- 这里我们EntityManagerFactory就相当于Hibernate中的SessionFactory,
然而EntityManager就相当于Hibernate中的Session。 - 在JPA中我们获得它的方式是Persistence.createEntityManagerFactory("jun");
jun是在persistence.xml中持久化单元名称 - EntityManager em = factory.createEntityManager();
其中EntityManager中的几种操作数据库方法有
Persist(),find(),getReference(),merge(),remove()这里我就列出几种常见的 - 这里说有的getReference()就相当于Hibernate中的Load() 支持Lazy加载,
getReference它会查找一个代理对象,当访问它的属性的时候它才会向数据库操作 - 但是值得注意的这个必须确保Session是打开状态
实体bean的几种状态
一、新建状态
new一个实体的时候它所处于的状态
二、托管状态
这个状态非常重要,当一个实体bean是托管状态的时候我们修改它的属性值得话,即使我们没有用到hibernate中的相关方法操作数据库,她也会同步到数据库里的,托管就是从数据库里面查询到的一个对象,记住,托管状态必须的和事物关联上来
em.getTransaction().begin();
person.setName("小刘");
em.getTransaction().commit();
em.close();
factory.close();
三、游离状态
当我们使用EntityManager中的clear()方法的时候,它会把事物管理器中的所有实体变成游离状态,
处在游离状态的实体bean,我们修改它的属性它不会同步到数据库中的
em.getTransaction().begin();
em.clear(); // 把实体管理器中的所有实体都变成游离状态
person.setName("老刘");
em.merge(person);
em.getTransaction().commit();
em.close();
factory.close();
四、关闭状态
就是当我们把Session关闭的时候,实体bean处于的状态
JPA中使用EJQL语句
查询
public void query() {
/**
* jun是在persistence.xml中持久化单元名称
*/
EntityManagerFactory factory=Persistence.createEntityManagerFactory("jun");
//--->>SessionFactory-->>session-->>begin
EntityManager em = factory.createEntityManager();
Query query = em.createQuery("select o from Person o where o.id=?1");
query.setParameter(1, 2);
//
/**
* Person person = (Person)query.getSingleResult();
* 这个就相当于Hibernate中的uniqueResult();
* 如果没有查询到数据的话会出错的,所有我们一般不这样做
* 我们先得到List 然后遍历它
*/
List list = query.getResultList();
for(Person person : list)
System.out.println("---------->>" + person.getName());
query = em.createQuery("select count(o) from Person o ");
Long count = (Long)query.getSingleResult();
System.out.println("---------->>" + count);
em.close();
factory.close();
}
删除查询(记得要开启事物)
public void deletequery() {
/**
* jun是在persistence.xml中持久化单元名称
*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Query query = em.createQuery("delete from Person o where o.id=?1");
query.setParameter(1, 2);
query.executeUpdate();
em.getTransaction().commit();
em.close();
factory.close();
}
修改查询(记得要开启事物)
public void updatequery() {
/**
* jun是在persistence.xml中持久化单元名称
*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Query query = em.createQuery("update from Person o set o.name=:name where o.id=:id");
query.setParameter("name", "小刘");
query.setParameter("id", 3);
query.executeUpdate();
em.getTransaction().commit();
em.close();
factory.close();
}
刷新方法
public void testgetPerson() {
/**
* jun是在persistence.xml中持久化单元名称
*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
//--->>SessionFactory-->>session-->>begin
EntityManager em = factory.createEntityManager();
Person person = em.find(Person.class, 1);
/**
* 1分钟内
* 比方说这里有人直接操作数据库改了数据,我们如何来得到person呢
* 有人说我们可以再执行一下find方法,这个事不可以的,因为你再次
* 执行find方法的时候EntityManager会中一级缓存中取得id是1的
* Person数据,也就是说不能得到最新的数据,那么如果我们想得到最
* 新的数据怎么办呢?JPA帮我们做了一个刷新的方法em.refresh(person);
* 通过它我们就可以得到最新的数据了
*/
em.refresh(person);
System.out.println("----->>"+person.getName());
em.close();
factory.close();
}
JPA中实现表间关联关系
一的多
@Entity
@Table(name="t_order")
public class Order {
@Id
@Column(length=200)
private String orderid;
@Column(nullable=false)
private Float amount = 0f;
/**
* cascade=CascadeType.REFRESH设置级联刷新
* CascadeType.PERSIST设置级联保存
* CascadeType.MERGE设置级联更新
* CascadeType.REMOVE设置级联删除
* CascadeType.ALL设置四种级联
* 这四种级联都是对应的四种方法才能起作用PERSIST(),MERGE(),REMOVE(),REFRESH()
* fetch=FetchType.LAZY支持LAzy加载,只有用到该属性的时候才会读集合数据
* 注意这时我们必须保持EntityManager是开着的,默认是延迟加载
* mappedBy="order"指定OrderItem类里面的order这个属性来维护关系
*/
@OneToMany(cascade={CascadeType.ALL},
fetch=FetchType.LAZY,
mappedBy="order")
private Set items = new HashSet();
// get set //
}
为了我们添加Set集合方便我们通常的情况下我们的写一个添加Set的方法
public void addOrderItem(OrderItem orderItem) {
orderItem.setOrder(this);//this就是Order
items.add(orderItem);
}
多的一
@Entity
@Table(name="t_orderitem")
public class OrderItem {
@Id
@GeneratedValue
private Integer id;
@Column(length=40,nullable=false)
private String productName;
@Column(nullable=false)
private Float sellPrice = 0f;
/**
* optional=false指定该字段不能空
* @JoinColumn(name="order_id")指明关联关系的子段名
* 即定义外键的名称
*
* */
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
@JoinColumn(name="order_id")
private Order order;
// get set //
}
测试
public void testCreate() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Order order = new Order();
order.setAmount(598f);
order.setOrderid(UUID.randomUUID().toString());
OrderItem orderItem1 = new OrderItem();
orderItem1.setProductName("篮球");
orderItem1.setSellPrice(256f);
order.addOrderItem(orderItem1);
OrderItem orderItem2 = new OrderItem();
orderItem2.setProductName("女人");
orderItem2.setSellPrice(800000f);
order.addOrderItem(orderItem2);
em.persist(order);
em.getTransaction().commit();
em.close();
factory.close();
}
这里我们并没有保存orderIterm
但是我们在数据库中依然能看到orderIterm的数据
保存到数据库里面了,这里就是设置级联保存的缘故
CascadeType.PERSIST设置级联保存
一对一
Person.java
@Entity
@Table(name="t_person")
public class Person {
@Id
@GeneratedValue
private Integer id;
@Column(length=10,nullable=false)
private String name;
@OneToOne(optional=false,cascade={CascadeType.ALL})
@JoinColumn(name="idcard_id")
private IDCard idCard;
public Person() {}
public Person(String name) {
this.name = name;
}
}
IDCard.java
@Entity
@Table(name="t_idcard")
public class IDCard {
@Id
@GeneratedValue
private Integer id;
@Column(length=18,nullable=false)
private String cardno ;
@OneToOne(mappedBy="idCard",
cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE})
private Person person;
public IDCard() {}
public IDCard(String cardno) {
this.cardno = cardno;
}
}
测试
public void testCreateTable() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Person person = new Person("小刘");
IDCard idCard = new IDCard("411521198409131915");
idCard.setPerson(person);
person.setIdCard(idCard);
em.persist(person);
em.getTransaction().commit();
em.close();
factory.close();
}
多对多
Student.java
@Entity
@Table(name="t_student")
public class Student {
@Id
@GeneratedValue
private Integer id;
@Column(length=10,nullable=false)
private String name;
/**
* @JoinTable(name="t_stu_tea",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="teacher_id"))
创建中间表来维护两个表的关系
* joinColumns=@JoinColumn(name="student_id")定义维护端在中间表中的关联字段
* inverseJoinColumns=@JoinColumn(name="teacher_id")定义被维护端在中间表中的关联字段
*
*/
@ManyToMany(cascade=CascadeType.REFRESH)
@JoinTable(name="t_stu_tea",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="teacher_id"))
private Set teachers = new HashSet();
public Student() {}
public Student(String name) {
this.name = name;
}
}
- 我们通常把set集合方法重写
public void addTeacher(Teacher teacher) {
this.teachers.add(teacher);
}
public void removeTeacher(Teacher teacher) {
if(this.teachers.contains(teacher)) {
this.teachers.remove(teacher);
}
}
Teacher.java
@Entity
@Table(name="t_teacher")
public class Teacher {
@Id
@GeneratedValue
private Integer id;
@Column(length=12,nullable=false)
private String name;
@ManyToMany(cascade=CascadeType.REFRESH,mappedBy="teachers")
private Set students = new HashSet();
public Teacher() {}
public Teacher(String name) {
this.name = name;
}
}
测试
@Test
public void testCreateTable() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Student student = new Student("小刘");
Teacher teacher = new Teacher("肖老师");
em.persist(student);
em.persist(teacher);
em.getTransaction().commit();
em.close();
factory.close();
}
/**
* 建立学生跟老师的关系
*/
@Test
public void buildTS() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Student student = em.find(Student.class, 1);
student.addTeacher(em.getReference(Teacher.class, 1));
em.getTransaction().commit();
em.close();
factory.close();
}
/**
* 解除学生跟老师的关系
*/
@Test
public void deleteTS() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Student student = em.find(Student.class, 1);
student.removeTeacher(em.getReference(Teacher.class, 1));
em.getTransaction().commit();
em.close();
factory.close();
}
/**
* 删除老师
*/
@Test
public void deleteTeacher() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
/**
* 删除老师的时候一定的解除掉老师和学生的关系
*/
Student student = em.find(Student.class, 1);
student.removeTeacher(em.getReference(Teacher.class, 1));
em.remove(em.getReference(Teacher.class, 1));
em.getTransaction().commit();
em.close();
factory.close();
}
/**
*删除学生
*/
@Test
public void deleteStudent() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
/**
* 这个为什么不需要解除关系呢,主要是因为学生这一段是维护端
*/
Student student = em.find(Student.class, 1);
em.remove(student);
em.getTransaction().commit();
em.close();
factory.close();
}
联合主键
我们单独写一个类作为主键,但是这个类的要满足3条规则
- 它必须实现Serializable接口
- 它必须重写字段的hashCode()和equals()方法
- 要在类上表明@Embeddable,这个注解的意思就是让它的属性在AirLine也正常做属性
那么我们在主体类里面用这个主键类做为id,在id上面加上@EmbeddedId表明就是用主键类的属性作为主键,并映射到数据库表中
AirLine.java
@Entity
@Table(name="t_airline")
public class AirLine {
@EmbeddedId
private AirLinePK id;
@Column(length=10,nullable=false)
private String name;
}
AirLinePK.java
@Embeddable
public class AirLinePK implements Serializable{
// 序列UID
private static final long serialVersionUID = -7125628704970675246L;
@Column(length=20)
private String startLine;
@Column(length=20)
private String endLine;
// 注意这里一定的时间他HashCode()和equals()方法
}
测试
public void testPK() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jun");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
AirLine airLine = new AirLine("PEK","SHG","北京飞往上海");
em.persist(airLine);
em.getTransaction().commit();
em.close();
factory.close();
}
使用步骤和上边spring配置版介绍基本一致,关键是要理解一对一、一对多、多对一、多对多的关联关系
推荐使用Spring Boot
配置Maven依赖
org.springframework.boot
spring-boot-starter-data-jpa
配置JPA
# maven打包 clean compile package
spring:
# 热重启
devtools:
restart:
enabled: true
# 数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///qnzf?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password:
# JPA配置
jpa:
show-sql: true
hibernate:
naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
ddl-auto: update
# tomcat
server:
port: 8080
tomcat:
Uri-encoding: UTF-8
通过实体类映射数据表
// LED.java
@Entity
@Table(name = "tbl_led")
public class LED {
public LED() {
super();
}
// 指定id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 唯一键
@Column(unique = true)
private Integer pin; // LED连接的针脚位
}
通过接口类继承使用JPA内封装实现的方法
// LEDRepository.java
public interface LEDRepository extends JpaRepository { }
最后可以在Controller类中使用
@Autowired
private LEDPowerRepository ledPowerRepository;
个别使用方法可以浏览其他教程介绍加强一下
Spring For All 社区 Spring Data JPA 从入门到进阶系列教程
SpringBoot实战SpringDataJPA
使用 Spring Data JPA 简化 JPA 开发