什么是JPA?
什么是JDBC知道吧?数据库有Mysql,SQL Server,Oracle,Java为了统一的去操作建立连接,JDBC出现了,有了JDBC,加载注册不同的数据库驱动就完事了。
但是,JDBC好繁琐的啊,每次都要写一堆,烦人,所以一些框架出现了,例如Hibernate,TopLink,但是这些框架也是各种各样的,我每次使用都要按照各自的框架来写,这不行,为了省事,JPA出现了
JPA是规范 JPA不是框架,没有实现的方法,就是规范,提供了一些API接口,你们这些hibernate,TopLink框架啥的,遵循我的JPA规范就成
顺便一提,JPA是Hibernate作者搞出来的,所以JPA和Hibernate一样,不需要你写SQL,有时候必须自己写SQL可以使用JPQL
PS:EJB有三种,分别是会话Bean(Session Bean),实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。在EJB3.0推出以后,实体Bean被单独分了出来,形成了新的规范JPA。
引入配置
想要使用JPA,就要引入一些配置,我的项目是SpringBoot,其他的框架也行,引入了三个,JPA,Hibernate,MySQL
<!--JPA,我用的是SpringBoot的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!--Hibernate,说了JPA只是一个规范,我们使用Hibernate作为实现-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!--数据库不能少-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
引入的配置完了,接下来是对JPA的一些配置,不能不写,不写不成功
### Java Persistence Api -- Spring jpa 的配置信息
#其实这个hibernate.hbm2ddl.auto参数的作用主要用于:自动创建|更新|验证数据库表结构,有四个值
#create: 每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
#create-drop :每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
#update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。
#validate :每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
#dialect 主要是指定生成表名的存储引擎为InneoDB
#show-sql 是否打印出自动生产的SQL,方便调试的时候查看
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true
这个是application.properties里面关于JPA的配置,数据库配置就不贴了,JPA配置的讲解见注释
新建一个Entity Bean类
package com.cache.entity;
import javax.persistence.*;
@Entity
@Table(name="product")
public class Product {
@Id
@GeneratedValue
@Column(name="id")
private Integer id;
private String name;
private Double price;
private String type;
public Product() {
}
public Product(Integer id, String name, Double price, String type) {
this.id = id;
this.name = name;
this.price = price;
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", type='" + type + '\'' +
'}';
}
}
这个为啥叫Entity Bean类呢?我讲一下,普通的Java Bean类加上@Entity注解之后就变成了Entity Bean类,这个Entity Bean的意思就是这个类可以映射到数据库
讲解一下上面Entity Bean里面的注解是什么意思
@Entity : 这个注解代表这个Userinfo是一个实体类,也就是说,当你创建实体类时,需要加上@Entity 标志着此类是实体类;
@Table(name=”userinfos”) :这个注解代表对应表的名称,userinfos就是利用jpa要生成的表名称;
@id :表明此属性是主键;
@GeneratedValue :表示的是 主键生成策略,默认主键是自增长的,当然你也可以使用 uuid
@Column :表明此属性在数据库中对应的字段,如果实体中的属性与数据库中的字段一样,可以省略不写。还有两个注解我没使用,可以了解一下:
@Transient:Entity Bean的属性或字段加上这个注解,该字段便不会映射到数据库表
@Temporal:也是在Entity Bean的属性或字段上加的,这个是对Date时间类型的精度的一个选择,例如生日我就想精确到年月日就行了,下单时间我却想精确到时分秒,这个时候就可以使用这个,有Date,TIMESTAMP,TIME这三种精度
怎么样?爽不爽?没有JDBC的繁琐,我仅仅是写了一个Entity Bean实体类,我就和数据库进行映射关系了,内存中的实体类对象映射到数据库了,这也叫持久化。
持久化是位于 JDBC 之上的一个更高层抽象。持久层将对象映射到数据库,以便在查询、 装载、 更新, 或删除对象的时候, 无须使用像 JDBC 那样繁琐的 API。
JPA的增删改查
新建操作接口
使用Entity Bean实体类,我们已经映射出了一个数据库表,现在要存储数据了,我们需要新建一个接口
package com.cache.repository;
import com.cache.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
public interface ProductRepository extends JpaRepository<Product,Integer> {
//由于JPA没有更新方法,所以需要自己写更新方法,我这里只写一个更新name的方法,如果想更新全部,可以第一个参数写类
@Transactional
@Modifying
@Query("update Product set name=?1 where id=?2")
int updateNameById(String name,Integer id);
}
我来好好的讲解一下这个接口,首先ProductRepository这个接口继承了
JpaRepository这个类,这个类里面有JPA的操作方法,所以只要我们的接口继承了这个类,我们也就有方法可以调用了,不需要自己写。
但是,JPA只有正删查,没有更改的方法,所以我们可以在接口里面自己写,我就写了一个更新Product实体类的name属性的方法
@Transactional 开启事务
@Modifying 修改方法专用注解
这两个注解
@Query(“update Product set name=?1 where id=?2”)
此注解书写JPql语句 其中 Product 是实体类名,name和 id 是实体类的属性名,?1代表是第一个参数 ?2是第二个参数
注意:JPql里面必须是实体类的类名,可不是你的数据库表名,如果写错了会报 XXX is not mapped 错误 |
新建测试类
在我们的ProductRepository接口上新建一个测试类,这个步骤不会的就太菜了
我直接贴出代码了,我已经写好了增删改查的代码了
package com.cache.repository;
import com.cache.entity.Product;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Optional;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
//增加数据
@Test
public void save(){
Product product=new Product();
product.setId(1);
product.setName("魅族16s");
product.setPrice(2899.0);
product.setType("手机类");
//调用save方法保存一个对象的时候,会返回该对象
Product p=productRepository.save(product);
//断言返回对象的值就是我们存进去的值
Assert.assertEquals(p.getName(),"魅族16s");
}
//删除一个数据
@Test
public void delete(){
productRepository.deleteById(1);
}
//更新,使用的是我们自己写的更新name方法
@Test
public void update(){
productRepository.updateNameById("魅族16s Plus",2);
}
//查找数据
@Test
public void selectAll(){
List<Product> lists=productRepository.findAll();
for (Product product : lists) {
System.out.println(product);
}
}
@Test
public void selectOne(){
Optional<Product> product=productRepository.findById(2);
System.out.println(product);
}
}
我都执行了一遍,都是ok的
总结
由于JPA是Hibernate实现的,所以不需要我们写SQL,都是自动的(除了JPQL)
简单的说,JPA对比Mybatis,还是简单的很多,没有配置文件了,不需要自己写SQL了,连操作方法都有JpaRepository类帮我们写好了
如果不是需要性能优化什么的,简单的使用来说,还是JPA好用