spring-data-jpa简介(转载)
转载:
https://blog.csdn.net/matafeiyanll/article/details/124603090
https://blog.csdn.net/qq_42495847/article/details/107991361
JPA全英文名叫Java Persistence API
,就是java持久化API,是SUN公司推出的一套基于ORM
的规范。
Sun引入新的JPA ORM规范出于两个原因:
其一,简化现有Java EE和Java SE应用开发工作;
其二,Sun希望整合ORM技术,实现天下归一。
Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。
一、JPA基础
整合SpringData JPA
1.导入jar包依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
2.添加配置文件
在application.yml中添加datasource配置,或者
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8 username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: true
属性解析:
ddl.auto 参数的作用主要用于:自动创建、更新、验证数据库表结构,有四个值。
create:每次加载 Hibernate 时都会删除上一次生成的表,然后根据 model 类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop:每次加载 Hibernate 时根据 model 类生成表,但是 sessionFactory 一关闭,表就自动删除。
update:最常用的属性,第一次加载 Hibernate 时根据 model 类会自动建立起表的结构(前提是先建立好数据库),以后加载 Hibernate 时根据 model 类自动更新表结构,即使表结构改变了,但表中的行仍然存在,不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate :每次加载 Hibernate 时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
创建一张entity对象
@Data @Entity @Table(name = "task_info") public class TaskInfo implements Serializable { private static final long serialVersionUID = 8247978712837006424L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String projectCode; @ApiModelProperty("job名称") private String jobName; @NotEmpty @ApiModelProperty("task名称") private String name; //前端传 private Long taskCode; String description; @NotEmpty @ApiModelProperty("任务类型") private String taskType;
这里面使用了一些JPA的注解,但JPA注解远不止这些。
基本注解
基本注解包括@Entity、@Table、@Id、@IdClass、@GeneratedValue、@Basic、@Transient、@Column、@Temporal、 @Enumerated、@Lob。
@Entity定义对象将会成为被JPA管理的实体,将映射到指定的数据库表。
@Table指定数据库的表名。
@Id定义属性为数据库的主键,一个实体里面必须有一个。
@IdClass利用外部类的联合主键。
@GeneratedValue为主键生成策略
@Basic表示属性是到数据库表的字段的映射。如果实体的字段上没有任何注解,默认即为@Basic。
@Transient表示该属性并非一个到数据库表的字段的映射,表示非持久化属性,与@Basic作用相反。JPA映射数据库的时候忽略它。
@Column定义该属性对应数据库中的列名。
@Temporal用来设置Date类型的属性映射到对应精度的字段。
@Lob 将属性映射成数据库支持的大对象类型,支持以下两种数据库类型的字段。
关联关系注解
@JoinColumn定义外键关联的字段名称
@OneToOne关联关系
@OneToMany与@ManyToOne可以相对存在,也可只存在一方。
@ManyToMany表示多对多,和@OneToOne、@ManyToOne一样也有单向、双向之分。单向双向和注解没有关系,只看实体类之间是否相互引用。
如果是使用jdbcTemplate或者mybatis,下一步肯定是要写SQL了。使用JPA不需要这一步,直接创建repository接口即可。
public interface TaskInfoRepository extends JpaRepository<TaskInfo, Long> { List<TaskInfo> findByProjectCodeAndJobName(String projectCode, String jobName); @Transactional void deleteByProjectCodeAndJobName(String projectCode, String jobName) ; }
二、批量操作
如果直接引入repository,你会发现它已经内置了很多方法,这些方法都用SQL写也都能实现,但不知道要写到猴年马月。
但是你会发现,repository提供了findAll的内置方法,但是我们还有一些saveAll(),updateAll(),deleteAll()的需求,要怎么办?
JPA也考虑到了这个问题,提供了EntityManager供我们使用
import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; @Service public class TaskInfoService { @PersistenceContext private EntityManager entityManager; //jpa 批量更新 @Transactional public void saveAll(List<TaskInfo> taskInfoList) { for (TaskInfo taskInfo : taskInfoList) { entityManager.persist(taskInfo); } } @Transactional public void deleteAll(List<TaskInfo> taskInfoList) { for (TaskInfo taskInfo : taskInfoList) { entityManager.remove(taskInfo); } } }
三、自定义SQL
这样不管是单个对象的curd,还是批量对象的增删改查,JPA都可以搞定!
但实际情况可能远比理论复杂,如果实际场景中JPA提供的方法不够用怎么办?
没关系,JPA也是支持自定义SQL的。
案例1:
@Repository public interface StockRepository extends JpaRepository<IndexCalculated, String> { @Query(nativeQuery = true, value = "SELECT `stock_code` AS `stockCode`,`stock_name` AS `stockName`,`stock_display_name` AS `stockDisplayName` FROM `stock_security`") List<Map<String, Object>> stockIndexInfoOfStock(); @Query(nativeQuery = true, value = "SELECT `index_code` AS `stockCode`,`index_name` AS `stockName`,`index_display_name` AS `stockDisplayName` FROM `index_info` WHERE `index_code` IN (SELECT `index_code` FROM `index_calculated`)") List<Map<String, Object>> stockIndexInfoOfIndex(); @Query(nativeQuery = true, value = "SELECT `stock_code` FROM `sector_stock` WHERE `sector_code` IN ?1") List<Object> stockOfSector(String[] sectorCodeArr); @Query(nativeQuery = true, value = "SELECT `name` AS `sectorName`,`stock_code` AS `stockCode` FROM `industry_sector`,`sector_stock` WHERE industry_sector.`code`=sector_stock.`sector_code`") List<Map<String, Object>> industryOfStock(); @Query(nativeQuery = true, value = "SELECT `index_code` AS `code`,`index_name` AS `name`,`index_display_name` AS `displayName` FROM `index_info` WHERE `index_code` IN (SELECT `index_code` FROM `index_calculated`)") List<Map<String, Object>> allIndexInfoOfCalculated(); @Transactional @Modifying @Query(nativeQuery = true, value = "INSERT INTO `index_calculated`(`index_code`,`update_time`) VALUES (?1,?2)") int updateIndexCalculated(String indexCode, String updateTime);
案例2:
//示例1 @Query("select t from Device t where t.deviceSn=:deviceSn and t.deleteFlag=1") Device findExistDevice(@Param("deviceSn") String deviceSn); //示例2 @Query("select t from Device t where t.deviceSn=:deviceSn and t.deviceType =:deviceType and t.deleteFlag=1") Device findExistDevice(@Param("deviceSn") String deviceSn,@Param("deviceType")Integer deviceType); //示例3 @Query("select t from Device t where t.deviceSn=?1 and t.deviceType = ?2 and t.deleteFlag=1") Device findDevice(String deviceSn,Integer deviceType); @Modifying @Query("update Device t set t.userName =:userName where t.id =:userId") User updateUserName(@Param("userId") Long userId,@Param("userName") String userName);
四、唯一键的更新
大家可以发现,jpa只有save,没有update,那我如果想update怎么办?这个时候分为两种情况
1.你有主键ID
jpa就是按照注解更新的,直接save即可
2.没有主键ID,有业务上的unique id
那就通过uniqueID先把对象查询出来,这个时候已经有主键ID了,将主键ID复制到原来要更新的对象上,save即可。
贴一下代码
@Test void save() { ProcessInfo byProjectCodeAndJobName = processInfoRepository.findByProjectCodeAndJobName("6012008297568", "job-111"); ProcessInfo processInfo = new ProcessInfo(); if (byProjectCodeAndJobName != null) { processInfo.setId(byProjectCodeAndJobName.getId()); } processInfo.setJobName("job-111"); processInfo.setProjectCode("6012008297568"); processInfo.setLocations("[{\"taskCode\":6076344071520,\"x\":213,\"y\":328},{\"taskCode\":6076344071521,\"x\":600,\"y\":281}]"); processInfo.setOwnership("default"); processInfo.setGlobalParams(""); processInfo.setDescription("job-111-update"); ProcessInfo save = processInfoRepository.save(processInfo); System.out.println(save); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2018-07-04 shell多线程(3)while循环