2.2 Spring Data通过JPA连接MySQL
JPA(Java Persistence API)是一套数据库持久层映射的规范,我们比较熟悉的Hibernate框架就是基于这套规范实现的,也就是说,它们两者的语法和开发方式非常相似。
这里,我们将通过Spring Data里的JPA实现组件来开发针对MySQL数据库的各种操作。
2.2.1 连接MySQL的案例分析
这里我们将实现通过JPA连接并访问MySQL数据库的整个流程。
1.创建数据表,构建Maven项目
我们在MySQL里创建一个名为springboot的数据库,在其中创建一个名为student的表,结构如表2.2所示。
表2.2 student表结构的说明
创建完表之后,我们再创建一个名为SpringBootJPAMySQLDemo的Maven类型的项目。
2.在pom.xml里配置要用到的包
本项目中pom.xml的关键代码如下,在其中将指定本项目要用到的jar包。
在第2~6行中,我们用parent标签来配置各子模块将要依赖的通用依赖包,也就是各子模块都要用到的jar包。注意,这里的版本是1.5.6.RELEASE。
在我们引入了第8~11行的依赖包后,我们就可以把本项目配置成Spring MVC了,比如通过@RestController来定义控制器。注意,在第5行里,我们已经定义了父类依赖包的版本号,这里就不必再重复定义了。
在第12~14行中,我们引入了Spring data jpa所必需的依赖包,其实就是所必需的jar文件。在第15到19行中,引入了mysql的驱动包。
在本项目里用到的jar包都存在于本地Maven仓库里,一旦在本项目的pom.xml里指定了要用到哪些jar包,就将根据具体指定的groupId和artifactId引用本地仓库里对应的包。
比如本机的maven本地仓库的路径是C:\Documents and Settings\Administrator\.m2\repository,而在pom.xml里配置mysql依赖包的代码如下:
那么本项目就会引用Maven本地仓库路径\mysql\mysql-connector-java\5.1.3目录下的jar包,如图2.2所示。其中,路径中的mysql和groupId相一致,mysql-connector-java和artifactId相一致,5.1.3和version相一致。
图2.2 Maven里被引用的jar实际位置示意图
同理,大家可以找到本项目引用到的jpa包的实际位置。
如果在本地仓库里找不到所需要的jar包,那么Maven会自动到远端仓库去下载jar包放置到本地仓库,比如本项目里用到的spring-boot-starter-web版本是1.5.6.RELEASE,如果本地没有,大家还能看到从远端仓库(一般是一个能提供各种Maven包的网站)下载的这个过程。
3.编写启动程序和控制器类
DataServerApp.java的代码如下,在其中的第5行里,我们编写了启动代码。不过请注意,它是放在jpademo这个package里的。
在studentController里,我们放置了控制器部分的代码,在其中我们通过@RequestMapping注解来指定request请求和待调用方法的对应关系。
在第7~10行里,定义了将被/find/name/{name}格式url触发的getStudentByName方法,其中是调用service类的方法,返回指定name的学生信息。
在第11~15行里,我们定义了可以被“/nameAndscore/{name}/{score}”这种url格式触发的findByNameAndScore方法,在其中,同样是通过调用service层的方法返回指定name和score的学生成绩。
在前文里已经提到,@SpringBootApplication注解包含了@ComponentScan,通过后者这个注解,我们能设置Spring容器的扫描范围。如果不设置,默认的扫描范围是本包(也就是jpademo)以及它的子目录。
这里我们需要让容器扫描带有@RestController的studentController类并把它设置成控制器类,如果把控制器类和App.java类设置成平级,那么容器会无法识别这个控制器,这就是为什么把控制器类包含在jpademo子目录里的原因。
同理,后面将要讲述的StudentService.java类,由于出现了@Autowired注解,因此也希望被容器扫描到,所以我们同样需要把该类放在jpademo的子目录里。
4.编写Service类
在StudentService.java里,我们编写提供业务服务的代码,上文里已经提到,为了也能让容器扫描到它,需要把它放在jpademo.servcie包(处于jpademo的子目录中)里,代码如下:
这个类里提供了两种服务方法,第8行的findByName方法实现了根据名字搜索的功能,第13行的findByNameAndScore方法实现了根据名字和分数搜索的功能。在这两个方法里,都是调用StudentRepository类型的stuRepository对象里的方法来实现功能的。
5.编写Repository类
在JPA里,一般是在Repository类放置连接数据库的业务代码,它的作用有些类似DAO。这里我们将在StudentRepository类里实现在刚才service层里调动的两个操作数据库的方法。
这里大家会看到一个比较有意思的现象,我们在第8行和第10行定义的两种方法都没有方法体。事实上在JPA的底层实现里将会根据方法名以及注解自动地执行查询语句并返回结果。
具体而言,在第8行的findByName方法里,将会执行第7行@Query注解所带的基于Student表的查询语句,并以List<Student>的形式返回结果。在第10行的findByNameAndScore方法里,JPA底层将解析方法名,以Name和Score这两个字段为条件查询Student表,同样以List<Student>的形式返回结果。
我们这里只给出了常用的通过equals查询的例子,在表2.3里,我们能看到JPA支持的其他常用关键字。
表2.3 JPA里支持的常用关键字列表
除此之外,JPA还支持isnull、like和OrderBy等其他查询关键字,但在项目里,简单查询的SQL语句毕竟是少数,在大多数查询语句里,往往会带3个以上关键字,比如:
select * from student where name=xxx and score>xxx and id in (xxx,xxx) order by id asc
在类似复杂的场景里,就无法直接使用上述“字段名+关键字”形式的方法了,这时就可以通过@Query引入较为复杂的SQL语句。注意,需要把nativeQuery设成true。具体代码如下:
1 @Query(value = 复杂的sql语句, nativeQuery = true) 2 List<Student> findStudent(String name,float score,String ids);
6.在配置文件里设置连接数据库的参数
在application.properties文件里,我们配置了MySQL数据库的各项连接参数,代码如下:
在第2行里,我们设置了数据表的创建方式,这里是update,在启动本项目时,Spring容器会把本地的映射文件和数据表做个比较,如果有差别,就用本地映射文件里的定义更新数据表结构,如果无差别,就什么也不做。这里如果没有特殊情况,不要用create,因为create的含义是“删除后再创建”,这样会导致数据表的数据丢失。
在第3~6行中,我们定义了连接url、用户名、密码和连接驱动等属性。
7.编写本地映射文件
由于Spring data JPA属于一种数据持久化映射技术,因此我们需要在本地开发一个能和Student数据表关联的Model对象,代码如下:
其中,我们通过第3行和第4行的注解来说明本类是用来映射Student表的;通过第6行的@Id注解,我们指定了第7行的ID属性是用来映射表里的主键ID的;通过类似于第8行的@Column注解,后面我们一一指定了本类里属性和Student数据表里的对应关系。
8.查看运行结果
至此,代码编写完成。运行前,我们需要到student表里插入一条name是tom、score是100.0的数据。通过DataServerApp.java启动web服务后,在浏览器里输入“http://localhost:8080/students/find/name/tom”,就会触发Controller层里的getStudentByName,在浏览器里能看到如下所示的结果。
[{"name":"tom","age":"12","score":100.0,"id":"1"}]
如果输入“http://localhost:8080/students/nameAndscore/tom/100”,就调用findByNameAndScore方法,也能看到同样的结果。
2.2.2 使用yml格式的配置文件
在刚才的例子里,我们是把配置文件写在.properties文件里,在项目里,我们还可以使用扩展名是yml的YAML文件来存放配置信息。
和传统的配置文件相比,yml文件结构性比较强,比较容易被理解,在企业级系统里也被广泛应用。
这里我们将在刚才SpringBootJPAMySQLDemo项目的基础上稍做修改,在其中将会用到yml文件来存放数据库的连接信息。
在这个项目里,需要去掉application.properties文件,在相同的位置添加一个application.yml文件,代码如下:
在上述文件里,我们能看到yml是用缩进来定义层级关系的。其中,第1~3行的代码等价于spring.jpa.show-sql = true,其他的配置信息以此类推。而且,建议在定义属性的冒号后面空一格再定义属性的值。
2.2.3 通过profile文件映射到不同的运行环境
我们在项目里经常会根据不同的运行环境使用不同的配置信息,比如在测试环境里连接测试数据库,在生产环境里连接生产库,又如,在测试和生产环境里往不同的位置输出日志信息。通过profile,我们能轻易地实现这种效果。
这个项目是在2.2.2小节的SpringBootJPAYMLDemo项目基础上修改而成的,这里我们将为QA和PROD环境配置不同的数据库连接参数。
修改点1,在application.yml里设置QA和PROD两个环境的配置信息,代码如下:
其中,第1~11行配置的是QA环境的信息,第13~23行配置的是PROD,中间用第12行的横线分隔,这个分隔符纯粹是为了提升可读性,开发中可以不加这个内容。上述代码的关键是在第2行和第14行里,用spring.profiles = XX的形式来指定该段代码的作用域。
修改点2,在启动文件App.java里,修改代码如下:
这里通过第6行的代码以.properties("spring.profiles.active=XX")的形式指定该以QA或PROD模式启动服务,从而指定本程序读取的是测试还是生产环境的数据库连接参数。