在数据拜访这章的第一篇文章《Spring 中使用 JdbcTemplate 拜访数据库》中,咱们现已介绍了怎么使用 SpringBoot 中最基本的 jdbc 模块来完结联系型数据库的数据读写操作。那么联合 Web 开发一章的内容,咱们就可能利用 JDBC 模块与 Web 模块的功能,综合着使用来完结一个实用于许多简略使用场景的后端使用了。
然而当咱们有必然的开发教训之后,不难发现,在实际开发过程中,对数据库的操作大多可能归结为:“增批改查”。就最为广泛的单表操作而言,除了表和字段不同外,句子几乎都是类似的,开发人员需要写许多类似而枯燥的句子来完结业务逻辑。
为了解决这些许多枯燥的数据操作句子,诞生了十分多的优异构造,比如:Hibernate。通过整合 Hibernate,咱们可能以操作 Java 实体的办法来完结对数据的操作,通过构造的帮助,对 Java 实体的改变最终将主动地映射到数据库表中。
在 Hibernate 的帮助下,Java 实体映射到数据库表数据完结之后,再进一步解决抽象各个 Java 实体基本的“增批改查”操作,咱们通常会以泛型的办法封装一个模板 Dao 来进行抽象简化,然而这样仍然不是很便当,咱们需要针对每个实体编写一个继承自泛型模板 Dao 的接口,再编写该接口的完结。只管一些根底的数据拜访现已可能失去很好的复用,然而在代码构造上针对每个实体都会有一堆 Dao 的接口和完结。
因为模板 Dao 的完结,使得这些具体实体的 Dao 层现已变的十分“薄”,有一些具体实体的 Dao 完结或者彻底便是对模板 Dao 的简略代理,而且往往这样的完结类或者会出现在许多实体上。SpringDataJPA 的出现正可能让这样一个现已很“薄”的数据拜访层变成仅仅一层接口的编写办法。比如,上面的比如:Java
publicinterfaceUserRepositoryextendsJpaRepository<User,Long>{UserfindByName(Stringname);@Query(“fromUseruwhereu.name=:name”)UserfindUser(@Param(“name”)Stringname);
}
咱们只需要通过编写一个继承自 JpaRepository 的接口就能完结数据拜访,上面以一个具体实例来体验 SpringDataJPA 给咱们带来的强健功能。
使用过程
因为 SpringDataJPA 依赖于 Hibernate。如果您对 Hibernate 有必然理解,上面内容可能毫不费力的看懂并上手使用它。如果您还是 Hibernate 老手,您可能先按如下办法入门,再倡议回头学习一下 Hibernate 以帮助这部分的了解和进一步使用。
工程装备
在 pom.xml 中减少相干依赖,退出以下内容:
<dependency<groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-data-jpaartifactId>dependency>
在 application.xml 中装备:数据库连接信息(如使用嵌入式数据库则不需要)、主动创立表构造的设置,例如使用 mysql 的情况如下:
spring.datasource.url=jdbc:mysql://localhost:3306/testspring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
spring.jpa.properties.hibernate.hbm2ddl.auto 是 hibernate 的装备特色,其首要作用是:主动创立、更新、验证数据库表构造。该参数的几种装备如下:
create:每次加载 hibernate 时都会删去上一次的生成的表,而后依据你的 model 类再从头来生成新表,哪怕两次没有任何改变也要这样履行,这便是导致数据库表数据失落的一个重要起因。
create-drop:每次加载 hibernate 时依据 model 类生成表,然而 sessionFactory 一敞开, 表就主动删去。
update:最罕用的特色,第一次加载 hibernate 时依据 model 类会主动建立起表的构造(条件是先建立好数据库),今后加载 hibernate 时依据 model 类自动更新表构造,即使表构造改变了但表中的行依然存在不会删去已经的行。要留神的是当安排到服务器后,表构造是不会被马上建立起来的,是要等使用第一次运行起来后才会。
validate:每次加载 hibernate 时,验证创立数据库表构造,只会和数据库中的表进行比拟,不会创立新表,然而会刺进新值。
至此现已完结根底装备,如果您有在 Spring 下整合使用过它的话,置信你现已感触到 SpringBoot 的便当之处:JPA 的传统装备在 persistence.xml 文件中,然而这儿咱们不需要。当然,最好在构建我的项目时候依照之条件过的最佳实际的工程构造来安顿,这样以确保各种装备都能被构造扫描到。
创立实体
创立一个 User 实体,蕴含 id(主键)、name(名字)、age(年纪)特色,通过 ORM 构造其会被映射到数据库表中,因为装备了 hibernate.hbm2ddl.auto,在使用动员的时候构造会主动去数据库中创立对应的表。
@Entity@Data@NoArgsConstructorpublicclassUser{@Id@GeneratedValueprivateLongid;privateStringname;privateIntegerage;publicUser(Stringname,Integerage){this.name=name;this.age=age;
}
}
@Entity 注解标识了 User 类是一个持久化的实体
@Data 和 @NoArgsConstructor 是 Lombok 中的注解。用来主动生成各参数的 Set、Get 函数以及不带参数的结构函数。如果您对 Lombok 还不理解,可能看看这篇文章:Java 开发神器 Lombok 的使用与原理
@Id 和 @GeneratedValue 用来标识 User 对应对应数据库表中的主键
留神:除了这些注解之外,还有许多用来精细化装备映射联系的注解,这儿不做具体介绍。后续会出专门一篇来介绍罕用注解。读者也可能自行阅览 Hibernate 的文档来学习这些注解的具体使用办法。
创立数据拜访接口
上面针对 User 实体创立对应的 Repository 接口完结对该实体的数据拜访,如下代码:
publicinterfaceUserRepositoryextendsJpaRepository<User,Long>{UserfindByName(Stringname);UserfindByNameAndAge(Stringname,Integerage);@Query(“fromUseruwhereu.name=:name”)UserfindUser(@Param(“name”)Stringname);
}
在 SpringDataJPA 中,只需要编写类似下面这样的接口就可完结数据拜访。不再像咱们以往编写了接口时候还需要本人编写接口完结类,间接缩小了咱们的文件清单。
上面对下面的 UserRepository 做一些解释,该接口继承自 JpaRepository,通过查看 JpaRepository 接口的 API 文档,可能看到该接口自身现已完结了创立(save)、更新(save)、删去(delete)、查问(findAll、findOne)等基本操作的函数,因而对于这些根底操作的数据拜访就不需要开发者再本人定义。
在咱们实际开发中,JpaRepository 接口定义的接口往往还不行或者性能不行优化,咱们需要进一步完结更芜杂一些的查问或操作。因为本文重点在 SpringBoot 中整合 spring-data-jpa,在这儿先抛砖引玉简略介绍一下 spring-data-jpa 中让咱们兴奋的功能,后续再独自开篇讲一下 spring-data-jpa 中的常见使用。
在上例中,咱们可能看到上面两个函数:
UserfindByName(Stringname)
UserfindByNameAndAge(Stringname,Integerage)
它们别离完结了按 name 查问 User 实体和按 name 和 age 查问 User 实体,可能看到咱们这儿没有任何类 SQL 句子就完结了两个条件查询方法。这便是 Spring-data-jpa 的一大个性:通过解析办法名创立查问。
除了通过解析办法名来创立查问外,它也提供通过使用 @Query 注解来创立查问,您只需要编写 JPQL 句子,并通过类似“:name”来映射 @Param 指定的参数,就像比如中的第三个 findUser 函数雷同。
SpringDataJPA 的能力远不止本文说到的这些,因为本文首要以整合介绍为主,对于 SpringDataJPA 的使用仅仅介绍了常见的使用办法。比如 @Modifying 操作、分页排序、原生 SQL 反对以及与 SpringMVC 的联合使用等等内容就不在本文中具体关上,这儿先挖个坑,后续再补文章填坑,如您对这些感兴趣可能器重我博客或简书,同样欢送大家留言沟通想法。
单元测验
在完结了下面的数据拜访接口之后,依照惯例便是编写对应的单元测验来验证编写的内容是否正确。这儿就不多做介绍,首要通过数据操作和查问来重复验证操作的正确性。
@RunWith(SpringRunner.class)@SpringBootTestpublicclassApplicationTests{@AutowiredprivateUserRepositoryuserRepository;@Testpublicvoidtest()throwsException{// 创立 10 条记录 userRepository.save(newUser(“AAA”,10));
userRepository.save(newUser(“BBB”,20));
userRepository.save(newUser(“CCC”,30));
userRepository.save(newUser(“DDD”,40));
userRepository.save(newUser(“EEE”,50));
userRepository.save(newUser(“FFF”,60));
userRepository.save(newUser(“GGG”,70));
userRepository.save(newUser(“HHH”,80));
userRepository.save(newUser(“III”,90));
userRepository.save(newUser(“JJJ”,100));// 测验 findAll, 查问所有记录 Assert.assertEquals(10,userRepository.findAll().size());// 测验 findByName, 查问名字为 FFF 的 UserAssert.assertEquals(60,userRepository.findByName(“FFF”).getAge().longValue());// 测验 findUser, 查问名字为 FFF 的 UserAssert.assertEquals(60,userRepository.findUser(“FFF”).getAge().longValue());// 测验 findByNameAndAge, 查问名字为 FFF 而且年纪为 60 的 UserAssert.assertEquals(“FFF”,userRepository.findByNameAndAge(“FFF”,60).getName());// 测验删去名字为 AAA 的 UseruserRepository.delete(userRepository.findByName(“AAA”));// 测验 findAll, 查问所有记录, 验证下面的删去是否胜利 Assert.assertEquals(9,userRepository.findAll().size());
}
}
拓宽阅览:对于 SpringData
SpringDataJPA 在 Spring 家族中实际上是一个二级我的项目,它隶属于 SpringData 这个尖端我的项目。读者可能看一下对于这个我的项目的介绍,它除了涵盖对联系型数据库的抽象之外,其实还有许多对其余数据存储中间件的完结,比如咱们罕用的 Redis、MongoDB、Elasticsearch 等。
如果再找几个我的项目看一下它们的简略示例,你会发现:不论你是要拜访什么数据存储产品,它们的编码方法几乎都是雷同的!这便是 SpringData 这个我的项目充斥魅力的中央!通过对数据拜访操作的抽象来屏蔽细节,用不同子项目的办法去完结细节。让开发者只需要学会使用 SpringData,就能便当不便的学会对各种数据存储的操作。所以,对于 SpringData,我是强烈推荐 Java 开发者们可能学、甚至读一下源码的重要构造。只管,当初来说许多大型互联网公司并不会抉择它(性能考量居多,能真正用好它的人不多)作为首要的开发构造,然而其背地的抽象思维对错常值得咱们学习的。而且,在做一些非高并发我的项目的时候,这几乎便是一个不便开发神器,它可能帮助咱们少写十分多的代码!