首先说明一下,这里使用的是Springboot2.2.6.RELEASE版本,由于Springboot迭代很快,所以要注意版本问题。
1、Spring Data是Spring提供的帮助操作数据的框架,Spring Data中的一个模块叫做Spring Data JPA,Spring Data JPA只是Spring Data框架下的一个基于JPA标准操作数据的模块,Spring Data JPA底层默认的使用的是Hibernate来做的JPA实现。Spring Data JPA核心能力就是基于JPA的标准对数据进行操作,极大简化了代码的编写,简化操作持久层的代码,直接编写接口就可以了。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.2.6.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.bie</groupId> 12 <artifactId>springboot-jpa</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>springboot-jpa</name> 15 <description>Demo project for Spring Boot</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <!-- springBoot 的启动器 --> 23 <dependency> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-starter-web</artifactId> 26 </dependency> 27 <!-- thymeleaf 的启动器 --> 28 <dependency> 29 <groupId>org.springframework.boot</groupId> 30 <artifactId>spring-boot-starter-thymeleaf</artifactId> 31 </dependency> 32 <!-- springBoot测试的启动器 --> 33 <dependency> 34 <groupId>org.springframework.boot</groupId> 35 <artifactId>spring-boot-starter-test</artifactId> 36 <scope>test</scope> 37 <!--<exclusions> 38 <exclusion> 39 <groupId>org.junit.vintage</groupId> 40 <artifactId>junit-vintage-engine</artifactId> 41 </exclusion> 42 </exclusions>--> 43 </dependency> 44 45 <!-- Spring Data JPA 的启动器 --> 46 <dependency> 47 <groupId>org.springframework.boot</groupId> 48 <artifactId>spring-boot-starter-data-jpa</artifactId> 49 </dependency> 50 51 <!-- mysql驱动包 --> 52 <dependency> 53 <groupId>mysql</groupId> 54 <artifactId>mysql-connector-java</artifactId> 55 </dependency> 56 57 <!-- druid连接池 --> 58 <dependency> 59 <groupId>com.alibaba</groupId> 60 <artifactId>druid</artifactId> 61 <version>1.1.10</version> 62 </dependency> 63 </dependencies> 64 65 <build> 66 <plugins> 67 <plugin> 68 <groupId>org.springframework.boot</groupId> 69 <artifactId>spring-boot-maven-plugin</artifactId> 70 </plugin> 71 </plugins> 72 </build> 73 74 </project>
在application.properties配置文件中配置数据库链接信息、数据库链接池、Spring Data JPA开启正向工程、在控制台打印sql语句。
1 # mysql的数据库驱动 2 spring.datasource.driver-class-name=com.mysql.jdbc.Driver 3 # mysql的链接 4 spring.datasource.url=jdbc:mysql://localhost:3306/ssm 5 # mysql的账号 6 spring.datasource.username=root 7 # mysql的密码 8 spring.datasource.password=123456 9 10 # druid连接池的配置 11 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 12 13 # Spring Data JPA,此配置可以在实体类中使用注解来创建数据表,开启正向工程 14 spring.jpa.hibernate.ddl-auto=update 15 # 在控制台打印sql语句 16 spring.jpa.show-sql=true
创建实体类Users。
1 package com.bie.springboot.po; 2 3 import javax.persistence.*; 4 5 @Entity // 表示该类是实体类 6 @Table(name = "tb_users") // 表示该实体类和数据表进行映射,name表示实体类和数据表进行映射 7 // 如果使用的是正向工程的话,name属性的值表示的是数据表的表名称。 8 public class Users { 9 10 @Id // 表示该字段是主键 11 @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键的生成策略 12 @Column(name = "id") // 表示实体类的字段和数据表的字段进行映射的关系,如果是正向工程的话,name的值就是数据表的字段名称 13 private Integer id;// 用户编号 14 15 @Column(name = "name") 16 private String name;// 用户姓名 17 18 @Column(name = "age") 19 private Integer age;// 用户年龄 20 21 @Column(name = "address") 22 private String address;// 用户地址 23 24 // alt + insert来生成构造器、setter\getter等等方法 25 public Integer getId() { 26 return id; 27 } 28 29 public void setId(Integer id) { 30 this.id = id; 31 } 32 33 public String getName() { 34 return name; 35 } 36 37 public void setName(String name) { 38 this.name = name; 39 } 40 41 public Integer getAge() { 42 return age; 43 } 44 45 public void setAge(Integer age) { 46 this.age = age; 47 } 48 49 public String getAddress() { 50 return address; 51 } 52 53 public void setAddress(String address) { 54 this.address = address; 55 } 56 57 @Override 58 public String toString() { 59 return "Users{ " + 60 "id=" + id + 61 ", name='" + name + '\'' + 62 ", age=" + age + 63 ", address='" + address + '\'' + 64 '}'; 65 } 66 67 public Users(String name, Integer age, String address) { 68 this.name = name; 69 this.age = age; 70 this.address = address; 71 } 72 73 public Users() { 74 } 75 }
创建数据交互层接口继承JpaRepository<T,ID>。
泛型参数1,T表示的是当前需要映射的实体类类型,当前需要映射的实体。
泛型参数2,ID表示需要映射的实体中的主键的类型,当前映射的实体中的OID的类型。
1 package com.bie.springboot.dao; 2 3 import com.bie.springboot.po.Users; 4 import org.springframework.data.jpa.repository.JpaRepository; 5 6 /** 7 * JpaRepository<T,ID> 8 * 泛型参数1,T表示的是当前需要映射的实体类类型,当前需要映射的实体。 9 * 泛型参数2,ID表示需要映射的实体中的主键的类型,当前映射的实体中的OID的类型。 10 */ 11 public interface UsersDao extends JpaRepository<Users, Integer> { 12 13 }
开始测试,测试代码,如下所示:
1 package com.bie.springboot; 2 3 import com.bie.springboot.dao.UsersDao; 4 import com.bie.springboot.po.Users; 5 import org.junit.jupiter.api.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.boot.test.context.SpringBootTest; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 11 @RunWith(SpringJUnit4ClassRunner.class) 12 @SpringBootTest(classes = SpringbootJpaApplication.class) // 启动器,将测试类和启动器整合在一起 13 class SpringbootJpaApplicationTests { 14 15 @Autowired 16 private UsersDao usersDao; 17 18 @Test 19 public void testSave() { 20 Users users = new Users(); 21 users.setAddress("北京市海淀"); 22 users.setAge(20); 23 users.setName("张三"); 24 this.usersDao.save(users); 25 } 26 27 }
如果下面报错,就在数据库链接URL后面根据下面所示的内容即可。
?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
1 . ____ _ __ _ _ 2 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 3 ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 4 \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 5 ' |____| .__|_| |_|_| |_\__, | / / / / 6 =========|_|==============|___/=/_/_/_/ 7 :: Spring Boot :: (v2.2.6.RELEASE) 8 9 2020-05-20 13:40:41.734 INFO 14492 --- [ main] c.b.s.SpringbootJpaApplicationTests : Starting SpringbootJpaApplicationTests on DESKTOP-V37QSSE with PID 14492 (started by biehl in D:\program\idea\IntelliJ IDEA 2019.1.3\workspace_idea\springboot-jpa) 10 2020-05-20 13:40:41.736 INFO 14492 --- [ main] c.b.s.SpringbootJpaApplicationTests : No active profile set, falling back to default profiles: default 11 2020-05-20 13:40:43.891 INFO 14492 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 12 2020-05-20 13:40:44.000 INFO 14492 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 96ms. Found 1 JPA repository interfaces. 13 2020-05-20 13:40:45.245 INFO 14492 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 14 2020-05-20 13:40:45.451 INFO 14492 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.12.Final 15 2020-05-20 13:40:45.813 INFO 14492 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations { 5.1.0.Final} 16 Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary. 17 2020-05-20 13:40:47.129 INFO 14492 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited 18 2020-05-20 13:40:47.326 ERROR 14492 --- [reate-173197870] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/biehl?useUnicode=true&characterEncoding=utf8, errorCode 0, state 01S00 19 20 java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support. 21 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.19.jar:8.0.19] 22 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.19.jar:8.0.19] 23 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) ~[mysql-connector-java-8.0.19.jar:8.0.19] 24 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) ~[mysql-connector-java-8.0.19.jar:8.0.19] 25 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73) ~[mysql-connector-java-8.0.19.jar:8.0.19] 26 at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:76) ~[mysql-connector-java-8.0.19.jar:8.0.19] 27 at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836) ~[mysql-connector-java-8.0.19.jar:8.0.19] 28 at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456) ~[mysql-connector-java-8.0.19.jar:8.0.19] 29 at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:246) ~[mysql-connector-java-8.0.19.jar:8.0.19] 30 at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:197) ~[mysql-connector-java-8.0.19.jar:8.0.19] 31 at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1558) ~[druid-1.1.10.jar:1.1.10] 32 at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1623) ~[druid-1.1.10.jar:1.1.10] 33 at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2468) ~[druid-1.1.10.jar:1.1.10] 34 Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support. 35 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_191] 36 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_191] 37 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_191] 38 at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_191] 39 at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61) ~[mysql-connector-java-8.0.19.jar:8.0.19] 40 at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:85) ~[mysql-connector-java-8.0.19.jar:8.0.19] 41 at com.mysql.cj.util.TimeUtil.getCanonicalTimezone(TimeUtil.java:132) ~[mysql-connector-java-8.0.19.jar:8.0.19] 42 at com.mysql.cj.protocol.a.NativeProtocol.configureTimezone(NativeProtocol.java:2118) ~[mysql-connector-java-8.0.19.jar:8.0.19] 43 at com.mysql.cj.protocol.a.NativeProtocol.initServerSession(NativeProtocol.java:2142) ~[mysql-connector-java-8.0.19.jar:8.0.19] 44 at com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:1310) ~[mysql-connector-java-8.0.19.jar:8.0.19] 45 at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:967) ~[mysql-connector-java-8.0.19.jar:8.0.19] 46 at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:826) ~[mysql-connector-java-8.0.19.jar:8.0.19] 47 ... 6 common frames omitted 48 49 2020-05-20 13:40:47.330 ERROR 14492 --- [reate-173197870] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/biehl?useUnicode=true&characterEncoding=utf8, errorCode 0, state 01S00
2、Spring Data JPA提供的核心接口。
2.1)、Repository接口,提供了方法名称命名查询方式。
1 package com.bie.springboot.dao; 2 3 import com.bie.springboot.po.Users; 4 import org.springframework.data.repository.Repository; 5 6 import java.util.List; 7 8 /** 9 * Repository接口的方法名称命名查询。 10 */ 11 public interface UsersRepositoryByName extends Repository<Users, Integer> { 12 13 /** 14 * 方法命名必须以findBy开头的,后面跟的是属性的名称,属性的名称必须大写,遵循驼峰式写法。 15 * findBy开头的,后面跟的就是查询条件。 16 * <p> 17 * 方法的名称必须要遵循驼峰式命名规则,findBy(关键字)+属性名称(首字母要大写)+查询条件(首字母大写)。 18 * 查询条件默认是等于。 19 * <p> 20 * 只要遵循findBy规则,有很多可以进行查询的条件。 21 * 22 * @param name 23 * @return 24 */ 25 public List<Users> findByName(String name); 26 27 /** 28 * @param name 29 * @return 30 */ 31 public List<Users> findByNameEquals(String name); 32 33 /** 34 * 根据姓名和年龄进行查询 35 * 36 * @param name 37 * @param age 38 * @return 39 */ 40 public List<Users> findByNameAndAge(String name, Integer age); 41 42 /** 43 * 根据姓名模糊查询 44 * 45 * @param name 46 * @return 47 */ 48 public List<Users> findByNameLike(String name); 49 }
测试代码,如下所示:
1 package com.bie.springboot; 2 3 import com.bie.springboot.dao.UsersDao; 4 import com.bie.springboot.dao.UsersRepositoryByName; 5 import com.bie.springboot.po.Users; 6 import org.junit.jupiter.api.Test; 7 import org.junit.runner.RunWith; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.boot.test.context.SpringBootTest; 10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 12 import java.util.List; 13 14 @RunWith(SpringJUnit4ClassRunner.class) 15 @SpringBootTest(classes = SpringbootJpaApplication.class) 16 // 启动器,将测试类和启动器整合在一起 17 class SpringbootJpaApplicationTests { 18 19 @Autowired 20 private UsersRepositoryByName usersRepositoryByName; 21 22 23 @Test 24 public void testFindByName() { 25 List<Users> byName = this.usersRepositoryByName.findByName("张三"); 26 System.out.println("Name: " + byName); 27 } 28 29 30 @Test 31 public void testFindByNameEquals() { 32 List<Users> byName = this.usersRepositoryByName.findByNameEquals("张三"); 33 System.out.println("Name: " + byName); 34 } 35 36 37 @Test 38 public void testFindByNameAndAge() { 39 List<Users> byName = this.usersRepositoryByName.findByNameAndAge("张三", 20); 40 System.out.println("Name: " + byName); 41 } 42 43 @Test 44 public void testFindByNameLike() { 45 List<Users> byName = this.usersRepositoryByName.findByNameLike("张%"); 46 System.out.println("Name: " + byName); 47 } 48 49 }
Repository接口,提供了基于@Query注解的查询与更新方式。
1 package com.bie.springboot.dao; 2 3 import com.bie.springboot.po.Users; 4 import org.springframework.data.jpa.repository.Modifying; 5 import org.springframework.data.jpa.repository.Query; 6 import org.springframework.data.repository.Repository; 7 8 import javax.transaction.Transactional; 9 import java.util.List; 10 11 /** 12 * Repository接口,提供了基于@Query注解的查询与更新方式。 13 */ 14 public interface UsersRepositoryQueryAnnotation extends Repository<Users, Integer> { 15 16 /** 17 * 默认支持的是HQL语句,不是标准的sql语句,底层需要进行转换,转换为标准的sql语句 18 * 19 * @param name 20 * @return 21 */ 22 @Query(value = "from Users where name = :name") 23 public List<Users> queryByNameUseHQL(String name); 24 25 /** 26 * 如果是SQL语句需要,nativeQuery = true表示的就是底层不需要进行sql语句的转换 27 * 28 * @param name 29 * @return 30 */ 31 @Query(value = "select * from tb_users where name = ?", nativeQuery = true) 32 public List<Users> queryByNameUseSQL(String name); 33 34 /** 35 * 更新操作,注意update后面是实体类的名称 36 * 37 * @param name 38 * @param id 39 */ 40 @Query(value = "update Users set name = :name where id = :id") 41 @Modifying // 该注解表示需要执行一个更新操作。注意此方法调用需要开通事务的 42 @Transactional // 开启事务 43 public void updateUsersNameById(String name, Integer id); 44 }
测试代码,如下所示:
1 package com.bie.springboot; 2 3 import com.bie.springboot.dao.UsersRepositoryQueryAnnotation; 4 import com.bie.springboot.po.Users; 5 import org.junit.jupiter.api.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.boot.test.context.SpringBootTest; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 11 import java.util.List; 12 13 @RunWith(SpringJUnit4ClassRunner.class) 14 @SpringBootTest(classes = SpringbootJpaApplication.class) 15 // 启动器,将测试类和启动器整合在一起 16 class SpringbootJpaApplicationTests { 17 18 @Autowired 19 private UsersRepositoryQueryAnnotation usersRepositoryQueryAnnotation; 20 21 @Test 22 public void testFindByNameUsrHQL() { 23 List<Users> byName = this.usersRepositoryQueryAnnotation.queryByNameUseHQL("张三"); 24 System.out.println("Name: " + byName); 25 } 26 27 @Test 28 public void testFindByNameUseSQL() { 29 List<Users> byName = this.usersRepositoryQueryAnnotation.queryByNameUseSQL("张三"); 30 System.out.println("Name: " + byName); 31 } 32 33 @Test 34 // @Transactional // 此方法调用需要添加事务,如果@Transactional注解和@Test注解配合使用,事务自动回滚 35 // @Rollback(value = false) // 取消自动回滚 36 public void testUpdateUsersNameById() { 37 this.usersRepositoryQueryAnnotation.updateUsersNameById("张飒飒2号", 1); 38 } 39 40 }
如果报如下所示的错误,需要将@Query(value = "from Users where name = ?")修改为@Query(value = "from Users where name = :name")。
1 . ____ _ __ _ _ 2 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 3 ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 4 \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 5 ' |____| .__|_| |_|_| |_\__, | / / / / 6 =========|_|==============|___/=/_/_/_/ 7 :: Spring Boot :: (v2.2.6.RELEASE) 8 9 2020-05-20 15:40:24.270 INFO 3352 --- [ main] c.b.s.SpringbootJpaApplicationTests : Starting SpringbootJpaApplicationTests on DESKTOP-V37QSSE with PID 3352 (started by biehl in D:\program\idea\IntelliJ IDEA 2019.1.3\workspace_idea\springboot-jpa) 10 2020-05-20 15:40:24.273 INFO 3352 --- [ main] c.b.s.SpringbootJpaApplicationTests : No active profile set, falling back to default profiles: default 11 2020-05-20 15:40:25.690 INFO 3352 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 12 2020-05-20 15:40:25.786 INFO 3352 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spri