1.Spring Boot:将JPA批量插入性能提高100倍
2.SpringDataJPA接口继承结构和底层原理、JPARepository接口使用和其它接口的使用
3.SpringDataJPAç使ç¨è¯¦è§£
4.Spring Data JPA(2)JPA详细操作流程和增删改查
5.SpringDataJpa打印Sql详情(含参数)
6.Spring Data JPA调用存储过程实例代码实例
Spring Boot:将JPA批量插入性能提高100倍
改善插入记录性能,Spring Data JPA 实现倍提升
面对数百万条记录的数据库导入需求,优化性能成为关键。本文分享了如何使用Spring Data JPA,将批量插入性能提升近倍的燕窝溯源码区分实现方法。
最初,使用spring JPA的saveAll方法进行批量插入时,每,条记录耗时约秒。通过一系列优化策略,性能显著提升。
首先,调整记录批处理大小。将原方法直接推送所有k记录改为批处理大小。设置hibernate属性batch_size=,性能从秒降至秒,提升约%。
接着,优化发送批处理记录的逻辑。按照属性文件配置,使方法批处理大小为,进一步提升了性能。邪恶图片源码
关键优化在于更改ID生成策略。原使用GenerationType.IDENTITY导致性能下降,因为此策略妨碍了批量更新,需要从数据库获取ID。改为SEQUENCE并提供序列生成器,使Hibernate能充分利用批量插入,性能提升近%。
为解决MySQL不支持序列生成器的问题,创建了一个表作为替代方案,表中包含一个名为next_val的字段和初始值,用于模拟序列生成器。
优化批处理大小,发现最佳值为1,,此时插入K记录仅需约4.秒。超过此值后,性能开始下降。
通过实践证明,上述策略综合应用可显著提升批量插入性能。原作者提供的GitHub存储库中可获取详细代码。
SpringDataJPA接口继承结构和底层原理、JPARepository接口使用和其它接口的dz内容变现源码使用
SpringDataJPA接口详解及其应用场景
Spring Data JPA提供了丰富的接口结构,以支持高效的数据操作。首先,我们来了解其继承结构:Spring Data JPA的接口主要基于Repository接口,它允许我们定义自定义的CRUD操作,如查询、插入、更新和删除。
底层原理方面,Spring Data JPA利用JPA(Java Persistence API)来连接数据库,通过方法命名规则和@Query注解执行动态查询,如使用JPQL(Java Persistence Query Language)或SQL进行条件筛选和更新操作。 Repository接口的核心是查询,支持多种方式:标准方法命名规则用于简单查询,@Query允许直接编写JPQL或SQL语句,@Modifying则用于执行更新操作。例如,你可以用@Query来根据用户名查找、筛选或更新用户信息。 CrudRepository接口提供基本的CRUD操作,PagingAndSortingRepository则支持分页和排序,例如通过Sort、angularjs源码分析1Order和Direction对象实现。JpaRepository在此基础上,通过JpaSpecificationExecutor接口扩展了更复杂的查询功能,支持多条件、分页和排序,大大增强了查询的灵活性。SpringDataJPAç使ç¨è¯¦è§£
JPA顾åæä¹å°±æ¯JavaPersistenceAPIçææï¼æ¯JDK5.0注解æXMLæ述对象ï¼å ³ç³»è¡¨çæ å°å ³ç³»ï¼å¹¶å°è¿è¡æçå®ä½å¯¹è±¡æä¹ åå°æ°æ®åºä¸ãSpringBoot使ç¨SpringDataJPAå®æCRUDæä½.æ°æ®çåå¨ä»¥å访é®é½æ¯æä¸ºæ ¸å¿çå ³é®é¨åï¼ç°å¨æå¾å¤ä¼ä¸éç¨ä¸»æµçæ°æ®åºï¼å¦å ³ç³»åæ°æ®åºï¼MySQLï¼Oracleï¼SQLServerãéå ³ç³»åæ°æ®åºï¼redisï¼mongodbç.
SpringDataJPAæ¯SpringDataçä¸ä¸ªå项ç®ï¼å®éè¿æä¾åºäºJPAçRepositoryæ大äºåå°äºæä½JPAç代ç ã
1ãå¯¼å ¥ç¸å ³ä¾èµå¹¶é ç½®æ件<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>spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl2ãJPA使ç¨æ¥éª¤ä¸ï¼æ°å»ºå®ä½ç±»å¹¶æ·»å JPA注解@Data@AllArgsConstructor@NoArgsConstructor@Entity@Table(name="article")publicclassArticleimplementsSerializable{ @Id@GeneratedValue@Column(name="a_id")privateIntegeraId;@Column(name="article_title")privateStringarticleTitle;@Column(name="article_content")privateStringarticleContent;@Column(name="head_image")privateStringheadImage;@Column(name="article_author")privateStringarticleAuthor;@Column(name="type_number")privateIntegertypeNumber;@Column(name="pageviews")privateIntegerpageViews;@Column(name="create_time")privateStringcreateTime;@Column(name="is_state")privateIntegerisState;}æ¥éª¤äºï¼æ°å»ºæ¥å£ArticleDao/***JpaRepository<T,ID>æä¾ç®åçæ°æ®æä½æ¥å£*Articleå®ä½ç±»ç±»å*Integer主é®ç±»å**JpaSpecificationExecutor<T>æä¾å¤ææ¥è¯¢æ¥å£*Articleå®ä½ç±»ç±»å**Serializableåºåå*/@RepositorypublicinterfaceArticleDaoextendsJpaRepository<Article,Integer>,JpaSpecificationExecutor<Article>,Serializable{ //è¿é没æ代ç ,注æ没æ代ç ..........}æ¥éª¤ä¸ï¼æµè¯@SpringBootTestclassSpringbootJpaApplicationTests{ @AutowiredprivateArticleDaoarticleDao;@TestvoidcontextLoads(){ List<Article>articleList=articleDao.findAll();articleList.forEach(System.out::println);}}3ãJPAæ¥è¯¢æ¹æ³å½ä»¤è§èå ³é®åæ¹æ³å½åsqlwhereåå¥AndfindByNameAndPwdwherename=?andpwd=?OrfindByNameOrSexwherename=?orsex=?Is,EqualsfindById,findByIdEqualswhereid=?BetweenfindByIdBetweenwhereidbetween?and?LessThanfindByIdLessThanwhereid<?LessThanEqualsfindByIdLessThanEqualswhereid<=?GreaterThanfindByIdGreaterThanwhereid>?GreaterThanEqualsfindByIdGreaterThanEqualswhereid>=?AfterfindByIdAfterwhereid>?BeforefindByIdBeforewhereid<?IsNullfindByNameIsNullwherenameisnullisNotNull,NotNullfindByNameNotNullwherenameisnotnullLikefindByNameLikewherenamelike?NotLikefindByNameNotLikewherenamenotlike?StartingWithfindByNameStartingWithwherenamelike'?%'EndingWithfindByNameEndingWithwherenamelike'%?'ContainingfindByNameContainingwherenamelike'%?%'OrderByfindByIdOrderByXDescwhereid=?orderbyxdescNotfindByNameNotwherename<>?InfindByIdIn(Collection<?>c)whereidin(?)NotInfindByIdNotIn(Collection<?>c)whereidnotin(?)TruefindByAaaTuewhereaaa=trueFalsefindByAaaFalsewhereaaa=falseIgnoreCasefindByNameIgnoreCasewhereUPPER(name)=UPPER(?)4ãJPQLè¯æ³çæpublicinterfaceStandardRepositoryextendsJpaRepository<Standard,Long>{ //JPAçå½åè§èList<Standard>findByName(Stringname);//èªå®ä¹æ¥è¯¢,没æéµå¾ªå½åè§è@Query("fromStandardwherename=?")StandardfindByNamexxxx(Stringname);//éµå¾ªå½åè§è,æ§è¡å¤æ¡ä»¶æ¥è¯¢StandardfindByNameAndMaxLength(Stringname,IntegermaxLength);//èªå®ä¹å¤æ¡ä»¶æ¥è¯¢@Query("fromStandardwherename=?2andmaxLength=?1")StandardfindByNameAndMaxLengthxxx(IntegermaxLength,Stringname);//使ç¨âæ åâSQLæ¥è¯¢,以åmysqlæ¯æä¹åï¼è¿é继ç»@Query(value="select*fromT_STANDARDwhereC_NAME=?andC_MAX_LENGTH=?",nativeQuery=true)StandardfindByNameAndMaxLengthxx(Stringname,IntegermaxLength);//模ç³æ¥è¯¢StandardfindByNameLike(Stringname);@Modifying//代表æ¬æä½æ¯æ´æ°æä½@Transactional//äºå¡æ³¨è§£@Query("deletefromStandardwherename=?")voiddeleteByName(Stringname);@Modifying//代表æ¬æä½æ¯æ´æ°æä½@Transactional//äºå¡æ³¨è§£@Query("updateStandardsetmaxLength=?wherename=?")voidupdateByName(IntegermaxLength,Stringname);}5ãJPAURUD示ä¾modleï¼Article.java
@Data@AllArgsConstructor@NoArgsConstructor@Entity@Table(name="article")publicclassArticleimplementsSerializable{ @Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="a_id")privateintaId;@Column(name="article_title")privateStringarticleTitle;@Column(name="article_content")privateStringarticleContent;@Column(name="head_image")privateStringheadImage;@Column(name="article_author")privateStringarticleAuthor;@Column(name="type_number")privateinttypeNumber;privateintpageviews;@DateTimeFormat(pattern="yyyy-MM-ddHH:mm:ss")@JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="GMT+8")@Column(name="create_time")privateDatecreateTime;@Column(name="is_state")privateintisState;}daoï¼ArticleDao.java
publicinterfaceArticleDaoextendsJpaRepository<Article,Integer>,JpaSpecificationExecutor<Article>,Serializable{ List<Article>findByArticleTitleContaining(Stringkeywords);//èªå®ä¹æ¹æ³@Query("selectartfromArticleartwhereart.articleTitlelike%?1%orart.articleContentlike%?1%")Page<Article>findByLike(Stringkeywords,Pageablepageable);}serviceï¼ArticleService.java
publicinterfaceArticleService{ Page<Article>findByLike(Stringkeywords,intpage,intpageSize);publicvoiddelArticle(intaId);publicvoidupdateArticle(Articlearticle);publicvoidaddArticle(Articlearticle);}serviceImplï¼ArticleServiceImpl.java
@ServicepublicclassArticleServiceImplimplementsArticleService{ @AutowiredprivateArticleDaoarticleDao;@OverridepublicPage<Article>findByLike(Stringkeywords,intpage,intpageSize){ Sortsort=Sort.by(Sort.Direction.DESC,"createTime");PageRequestpageable=PageRequest.of(page-1,pageSize,sort);Page<Article>pageResult=articleDao.findByLike(keywords,pageable);returnpageResult;}@OverridepublicvoiddelArticle(intaId){ articleDao.deleteById(aId);}@OverridepublicvoidupdateArticle(Articlearticle){ articleDao.save(article);}@OverridepublicvoidaddArticle(Articlearticle){ articleDao.save(article);}}controllerï¼ArticleController.java
spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImplãJPAå®ç°å页å模ç³æ¥è¯¢modleï¼Article.java
@Data@AllArgsConstructor@NoArgsConstructor@Entity@Table(name="article")publicclassArticleimplementsSerializable{ @Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column(name="a_id")privateintaId;@Column(name="article_title")privateStringarticleTitle;@Column(name="article_content")privateStringarticleContent;@Column(name="head_image")privateStringheadImage;@Column(name="article_author")privateStringarticleAuthor;@Column(name="type_number")privateinttypeNumber;privateintpageviews;@DateTimeFormat(pattern="yyyy-MM-ddHH:mm:ss")@JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="GMT+8")@Column(name="create_time")privateDatecreateTime;@Column(name="is_state")privateintisState;}daoï¼ArticleDao.java
spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl2serviceï¼ArticleService.java
spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl3serviceImplï¼ArticleServiceImpl.java
spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl4controllerï¼ArticleController.java
spring:datasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:username:password:jpa:database:mysql#æ¥å¿ä¸æ¾ç¤ºsqlè¯å¥show-sql:truehibernate:naming:physical-strategy:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl5Spring Data JPA(2)JPA详细操作流程和增删改查
操作步骤包括以下几步:
1. 加载配置文件并创建实体管理器工厂。使用Persistence的静态方法,通过传入持久化单元名称来创建实体管理器工厂。
2. 基于实体管理器工厂创建实体管理器。EntityManagerFactory负责获取EntityManager对象,其内部维护数据库信息、缓存信息和所有实体管理器对象。创建过程中会根据配置创建数据库表。EntityManagerFactory的创建较为资源密集,是线程安全的,多个线程可安全访问同一个对象。
3. 创建事务对象并开启事务。EntityManager对象用于实体类管理,支持beginTransaction创建事务对象,presist保存,爆客神器源码merge更新,remove删除,find/getReference根据id查询。Transaction对象用于事务管理,包含begin开启事务、commit提交事务和rollback回滚操作。
4. 进行增删改查操作。
5. 提交事务。
6. 释放资源。
JpaUtils工具类通过静态代码块在程序首次访问时创建一个公共的实体管理器工厂对象。首次调用getEntityManager方法时,通过静态代码块创建factory对象,并调用方法创建EntityManager对象。再次调用getEntityManager方法时,直接使用已创建的factory对象创建EntityManager对象。
查询方式包括立即加载和懒加载。使用find方法查询时,查询的对象即当前客户对象本身,且在调用find方法时立即发送SQL语句查询数据库。使用getReference方法查询时,获取的对象是动态代理对象,调用getReference方法不会立即发送SQL语句查询数据库,而是在调用查询结果对象时才发送SQL语句。
SpringDataJpa打印Sql详情(含参数)
在Spring Data Jpa应用中打印SQL详情,包括参数,采用的是log4jdbc工具。首先,调整pom文件引入并修改yml配置文件中的数据源配置,确保指向log4j相关驱动和URL。
配置文件中,重点修改driver-class-name与URL,指向log4jdbc相关的驱动与URL,以适配log4jdbc的使用。
接着,在项目resources目录下新建log4jdbc.log4j2.properties配置文件,定义log4jdbc的日志记录规则,以确保SQL执行过程中的信息被正确捕获。
配置完成后,启动项目,此时能观察到完整的SQL语句及参数。然而,日志中可能包含了大量不必要信息。通过过滤log4jdbc的audit、resultsettable、connection、sqltiming、sqlonly等包的日志,可以减小日志的冗余,提升日志的清晰度。
在实际项目中,为了妥善处理系统日志,通常需要添加logback配置文件。通过logback配置,不仅能够过滤掉冗余的日志信息,还能实现日志的保存与归档,便于后期的审计与问题追踪。
总结,通过上述步骤,实现了Spring Data Jpa应用中SQL执行过程的详细日志记录与管理,不仅包括SQL语句与参数信息,还能够根据实际需要调整日志的输出内容与保存策略,提高日志的实用性和管理效率。
Spring Data JPA调用存储过程实例代码实例
JPA连接到数据库,调用存储过程,这样的需求很常见。本文就针对这一点,讲述如何使用spring Data JPA调用存储过程的方法。
1、存储过程
假设存储过程如下:
CREATE OR REPLACE PACKAGE test_pkg AS
PROCEDURE in_only_test (inParam1 IN VARCHAR2);
PROCEDURE in_and_out_test (inParam1 IN VARCHAR2, outParam1 OUT VARCHAR2);
END test_pkg;
/
CREATE OR REPLACE PACKAGE BODY test_pkg AS
PROCEDURE in_only_test(inParam1 IN VARCHAR2) AS
BEGIN
DBMS_OUTPUT.PUT_LINE('in_only_test');
END in_only_test;
PROCEDURE in_and_out_test(inParam1 IN VARCHAR2, outParam1 OUT VARCHAR2) AS
BEGIN
outParam1 := 'Woohoo Im an outparam, and this is my inparam ' || inParam1;
END in_and_out_test;
END test_pkg;
这里有两个存储过程:
1)in_only_test
它需要一个输入参数inParam1,但不返回值
2)in_and_out_test
它需要一个输入参数inParam1,且返回值outParam1
2、@NamedStoredProcedureQueries
我们可以使用@NamedStoredProcedureQueries注释来调用存储过程。
@Entity
@Table(name = "MYTABLE")
@NamedStoredProcedureQueries({
@NamedStoredProcedureQuery(name = "in_only_test", procedureName = "test_pkg.in_only_test", parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "inParam1", type = String.class) }),
@NamedStoredProcedureQuery(name = "in_and_out_test", procedureName = "test_pkg.in_and_out_test", parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "inParam1", type = String.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "outParam1", type = String.class) }) })
public class MyTable implements Serializable {
}
关键要点:
存储过程使用了注释@NamedStoredProcedureQuery,并绑定到一个JPA表。
procedureName是存储过程的名字
name是JPA中的存储过程的名字
使用注释@StoredProcedureParameter来定义存储过程使用的IN/OUT参数
3、创建Spring Data JPA数据库
下面我们来创建Spring Data JPA数据库:
public interface MyTableRepository extends CrudRepository
@Procedure(name = "in_only_test")
void inOnlyTest(@Param("inParam1") String inParam1);
@Procedure(name = "in_and_out_test")
String inAndOutTest(@Param("inParam1") String inParam1);
}
关键要点:
@Procedure的name参数必须匹配@NamedStoredProcedureQuery的name
@Param必须匹配@StoredProcedureParameter注释的name参数
返回类型必须匹配:in_only_test存储过程返回是void,in_and_out_test存储过程必须返回String
4、调用
我们可以这样调用存储过程:
// 向存储过程传递参数并返回值
String inParam = "Hi Im an inputParam";
String outParam = myTableRepository.inAndOutTest(inParam);
Assert.assertEquals(outParam, "Woohoo Im an outparam, and this is my inparam Hi Im an inputParam");
// 向存储过程传递参数不返回值
myTableRepository.inOnlyTest(inParam);
5、其它技巧
如果上面的代码不工作,可以这么解决。定义自定义的Repository来调用存储过程昨晚本地查询。
定义自定义的Repository:
public interface MyTableRepositoryCustom {
void inOnlyTest(String inParam1);
}
然后要确保主Repository类继承了这个接口。
复制代码 代码如下:
public interface MyTableRepository extends CrudRepository
6、创建Repository实现类
接着该创建Repository实现类了:
public class MyTableRepositoryImpl implements MyTableRepositoryCustom {
@PersistenceContext
private EntityManager em;
@Override
public void inOnlyTest(String inParam1) {
this.em.createNativeQuery("BEGIN in_only_test(:inParam1); END;").setParameter("inParam1", inParam1)
.executeUpdate();
}
}
可以以常规的方式进行调用:
@Autowired
MyTableRepository myTableRepository;
// 调用存储过程
myTableRepository.inOnlyTest(inParam1);
JPA系列(二):Spring Jpa Specification 使用示例
在Spring Data JPA中,我们使用JpaSpecificationExecutor接口实现复杂的查询,只需简单地实现toPredicate方法。关键在于构建Predicates,通过使用and和or操作符,结合equal和other方法,实现复合查询。以下示例通过运动员表(player)和助手表(assistant)的关系进行学习。
假设存在两个实体类:PlayerEntity和AssistantEntity,它们之间是一对多的关系。通过以下数据库数据情况,我们来学习如何使用Specification查询。
在实现查询时,首先需要注入相应的Repository,如PlayerRepo。然后,使用CriteriaBuilder来构建复杂的查询条件,如等同于SQL中的and和or操作。
例如,我们查询player表,可以使用以下代码:
通过Predicate对象构建查询条件,如使用CriteriaBuilder.equal和CriteriaBuilder.and来添加等同条件,最终将其放入toPredicate方法中。
访问接口后,查询结果与SQL语句对应,例如:"select * from player where country = '中国' and profession = '篮球' and golden_num = 5;"。
为了展示混合查询,可以修改查询条件为:
结果对应的SQL为:"select * from player where country = '中国' or profession = '篮球' or golden_num = 5;"。
进一步地,可以添加like查询、大于、小于等条件,如查询"名字包含詹姆斯"的学生:
对应的SQL语句为:"select * from player where name like '%詹姆斯%';"。
此外,区间查询可以使用between操作符,例如:"select * from player where golden_num between 2 and 4;"。
通过上述方法,可以实现Spring JPA Specification的复杂查询。注意,为了避免循环查找导致的栈溢出异常,应返回包装类而非直接返回实体类。