上一篇中咱们已经介绍了在SpringBoot项目中怎么修改默认配置参数,而且咱们还掌握了怎么获取配置文件中自定义参数。在这一篇中咱们将介绍SpringBoot对数据库的操做。既然是对数据库的操做,那不免有一些配置的参数。例如数据库的链接、数据库帐号及数据库密码等。因此掌握上篇中的内容很重要。除此以外,咱们还要介绍一下用什么样的技术来操做数据库。操做数据库的技术有不少例如比较常见的JDBC、Mybatis、Hibernate等。在SpringBoot的项目中为咱们提供了另一种操做数据库的技术,也就是JPA。咱们能够经过JAP中提供的方式来很是方便的操做数据库。下面咱们首先添加SpringBoot对数据库的配置参数。具体参数以下:html
spring: profiles: active: dev datasource: url: jdbc:mysql://localhost:3306/springboot?useSSL=false&characterEncoding=utf8 username: root password: jilinwula driver-class-name: com.mysql.jdbc.Driver jpa: hibernate: ddl-auto: create show-sql: true
上面的配置参数比较简单,咱们就不详细介绍了,咱们只介绍spring.jpa.hibernate.ddl-auto参数。该参数的做用是自动操做表结构。且该参数有4种选项,下面咱们详细介绍一下这4种的区别。java
若是咱们按照上面的方式配置完后,则会发现上面的driver-class-name参数会报红显示,缘由是没有找到相关的依赖。而且在SpringBoot的项目中若是想用JPA功能的除了要引入Mysql的依赖外,还要引入Jpa的依赖。具体依赖以下:mysql
<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>
当咱们添加完上面的依赖后发现配置文件中的driver-class-name参数已经不报红显示了,这就表示咱们的依赖引入成功了。git
下面咱们建立一个实体类,来验证一下,上面的spring.jpa.hibernate.ddl-auto参数是否是上面说的那样。咱们首选验证当参数为create时。下面为实体类的代码:github
package com.jilinwula.springboot.helloworld.entity; import lombok.Data; import javax.persistence.*; @Data @Entity @Table(name = "user_info") public class UserInfoEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; }
上面实体类中咱们指定了几个注解,下面咱们详细介绍一下它们的做用:web
如今咱们一切准备就绪了,咱们只要启动一下SpringBoot的项目就能够了,看看会不会自动为咱们建立一张userinfo的表。(备注:数据库须要咱们本身建立)。咱们首先确认一下数据库中确实没有userinfo这张表。spring
下面咱们启动一下SpringBoot的项目,看一下数据库中有没有建立新的表。sql
咱们看JPA确实为咱们建立了一张和实体类中@Table注解指定的表,而且表中的字段和实体类中的属性一致。下面咱们手动向表中添加一条数据,而后从新启动项目,看看项目启动后,这条新增的数据仍是否存在。数据库
咱们只新增了一条数据,而后重启启动项目后,在看一下数据中的userinfo表,看看该数据还有没有。springboot
咱们发现刚刚添加的那条数据已经没有了,这也就偏偏证实了,咱们上面全部说当spring.jpa.hibernate.ddl-auto参数为create时,每当项目启动时,都会将原先的表删除,而后在经过实体类从新生成新的表,既然已是将原先的表都删除了,那曾经添加的数据固然不存在了。若是咱们仔细查看SpringBoot项目的启动日志,发现启动日志中已经输出了相应的删除及建表的语句,下面为项目启动的时操做表的日志。
Hibernate: drop table if exists user_info Hibernate: create table user_info (id bigint not null auto_increment, password varchar(255), username varchar(255), primary key (id))
下面咱们将spring.jpa.hibernate.ddl-auto参数修改成create-drop来验证一下create-drop参数的特性。咱们首先先把表删除掉,以避免刚刚的create参数影响create-drop的验证。
咱们仍是和刚刚同样启动项目后查看数据库中是否是自动为咱们建立一张userinfo表。
咱们看同样仍是自动为咱们建立了一个新的表。下面咱们将服务执行后,在看一下该表仍是不是存在。
咱们发现,刚刚建立的表已经自动删除了,这就是create-drop参数的特性。咱们查看日志,也能够发现当中止服务时,自动执行的删表语句。下面的日志。
Hibernate: drop table if exists user_info Hibernate: create table user_info (id bigint not null auto_increment, password varchar(255), username varchar(255), primary key (id)) 2019-01-18 16:56:27.033 INFO 6956 --- [ main] org.hibernate.tool.hbm2ddl.SchemaExport : HHH000230: Schema export complete 2019-01-18 16:56:27.058 INFO 6956 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2019-01-18 16:56:27.400 INFO 6956 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@21de60b4: startup date [Fri Jan 18 16:56:23 CST 2019]; root of context hierarchy 2019-01-18 16:56:27.482 INFO 6956 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2019-01-18 16:56:27.483 INFO 6956 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2019-01-18 16:56:27.511 INFO 6956 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2019-01-18 16:56:27.512 INFO 6956 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2019-01-18 16:56:27.553 INFO 6956 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2019-01-18 16:56:27.888 INFO 6956 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2019-01-18 16:56:27.973 INFO 6956 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8081 (http) 2019-01-18 16:56:27.978 INFO 6956 --- [ main] JilinwulaSpringbootHelloworldApplication : Started JilinwulaSpringbootHelloworldApplication in 5.928 seconds (JVM running for 7.512) 2019-01-18 17:00:50.630 INFO 6956 --- [ Thread-16] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@21de60b4: startup date [Fri Jan 18 16:56:23 CST 2019]; root of context hierarchy 2019-01-18 17:00:50.661 INFO 6956 --- [ Thread-16] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown 2019-01-18 17:00:50.664 INFO 6956 --- [ Thread-16] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2019-01-18 17:00:50.666 INFO 6956 --- [ Thread-16] org.hibernate.tool.hbm2ddl.SchemaExport : HHH000227: Running hbm2ddl schema export Hibernate: drop table if exists user_info
在日志中咱们发现一共执行了两次删除表的语句,第一次是在启动前,第二次是在服务中止时。
下面咱们把spring.jpa.hibernate.ddl-auto参数修改成update来验证update参数的特性。一样咱们仍是事先要把刚刚生成的表删除掉,由于create-drop参数在中止服务时,已经把刚刚的表删除掉了,因此咱们就不用手动删除了,咱们直接把spring.jpa.hibernate.ddl-auto参数修改成update,而后直接启动项目。
咱们看上图当咱们把spring.jpa.hibernate.ddl-auto参数设置为update时,也会自动为咱们建立表。而且咱们中止服务时,该表依然还存在,而且不会清除数据。下面咱们向表中添加一条新数据。而后修改实体类中的结构,看看刚刚新增的数据还存在不存在。
实体类修改以下:
package com.jilinwula.springboot.helloworld.entity; import lombok.Data; import javax.persistence.*; @Data @Entity @Table(name = "user_info") public class UserInfoEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String nickname; }
咱们新增了一个nickname属性,而后咱们从新启动服务看看这个新的字段会不会自动在表中建立,而且没有更改原先表中的数据。
咱们看新的字段已经自动建立成功了,而且没有删除原先表中的数据,这就是update参数的做用,在实际的开发中,把spring.jpa.hibernate.ddl-auto参数设置为update,是比较常见的配置方式。
下面咱们验证最后一个参数也就是validate参数。由于以前的操做咱们如今userinfo表中一其有3个字段,如今咱们将实体类中的nickname字段注释掉,而后咱们在启动服务,看一看项目启动是否正常。下面为实体类源码:
import javax.persistence.*; @Data @Entity @Table(name = "user_info") public class UserInfoEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; // private String nickname; }
当咱们启动项目时,发现项目是能够正常启动。而后查看数据库中表,咱们发现表中的结构没有任何变化。也就是说,咱们注释掉数据库中已有的字段而后启动项目时,项目是能够正常启动的。这又是为何呢?validate参数的做用不就是验证明体类中的属性与数据库中的字段不匹配时抛出异常吗?为何当咱们这么设置时没有抛出异常呢?这是由于validate参数的特性是只有实体类中的属性比数据库中的字段多时才会报错,如实体类中的属性比数据库中字段少,则不会报错。刚刚咱们将nickname属性给注释了,但validate是不会更改表结构的,因此数据库中仍是会有nickname字段的,这就致使数据中的字段比实体类中的属性多,因此当咱们启动项目时是不会抛出异常。但反之,若是咱们在实体类中新增一个字段,而后咱们在启动项目时,项目就会抛出异常。实体类源码:
@Data @Entity @Table(name = "user_info") public class UserInfoEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String nickname; private Long roleId; }
咱们新增了一个roleId字段,而且该字段在数据库中是没有的,而后咱们启动项目。查看日志发现项目已经启动失败了。下面为日志:
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [role_id] in table [user_info] at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateTable(SchemaValidatorImpl.java:85) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] at org.hibernate.tool.schema.internal.SchemaValidatorImpl.doValidation(SchemaValidatorImpl.java:50) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:91) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:475) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final] at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.3.21.RELEASE.jar:4.3.21.RELEASE] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360) ~[spring-orm-4.3.21.RELEASE.jar:4.3.21.RELEASE] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:384) ~[spring-orm-4.3.21.RELEASE.jar:4.3.21.RELEASE] ... 20 common frames omitted
下面咱们在数据库中将roleId字段手动添加上,而后咱们在从新启动项目,在看一下启动时项目仍是否报错。
下面咱们从新启动项目,而后在看一下日志,发现项目已经成功启动了,这就是validate参数的做用。
2019-01-19 17:17:41.160 INFO 1034 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2019-01-19 17:17:41.201 INFO 1034 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8081 (http) 2019-01-19 17:17:41.204 INFO 1034 --- [ main] JilinwulaSpringbootHelloworldApplication : Started JilinwulaSpringbootHelloworldApplication in 2.596 seconds (JVM running for 3.233)
上述内容咱们基本已经将spring.jpa.hibernate.ddl-auto参数的的使用介绍完了,下面咱们介绍一下实体类中的高级注解。由于咱们在上面的测试中咱们发现,当咱们把spring.jpa.hibernate.ddl-auto参数设置为create时,虽然成功的建立了实体类中指定的表,可是咱们发现自动建立的表只是字段和实体类中的属性一致,但例如表中的字段长度、字段的描述、表的索引,这些高级的配置,是须要咱们在实体类中添加新的注解,才能设置的。下面咱们将实体类中的代码修改一下,添加上面说的注解,而且验证上面注解是否能够正确设置表中的长度、描述及索引。(备注:别忘记将spring.jpa.hibernate.ddl-auto参数设置为create)下面为实体类源码:
package com.jilinwula.springboot.helloworld.entity; import lombok.Data; import javax.persistence.*; @Data @Entity @Table(name = "user_info", indexes = @Index(columnList = "username")) public class UserInfoEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", columnDefinition = "bigint(10) comment '主键'") private Long id; @Column(name = "username", columnDefinition = "varchar(10) not null default '' comment '帐号'") private String username; @Column(name = "password", columnDefinition = "varchar(10) not null default '' comment '密码'") private String password; @Column(name = "nickname", columnDefinition = "varchar(10) not null default '' comment '妮称'") private String nickname; @Column(name = "role_id", columnDefinition = "bigint(10) not null default 0 comment '角色'") private Long roleId; }
上面咱们介绍过当在类中添加@Entity注解后,JPA会自动将实体类中的属性映射为数据库中表里的字段。但在实际的开发中咱们可能会遇到实体类中的属性与数据库中的字段不一致的状况。这时咱们就要使用@Column注解了,该注解的参数有不少,咱们要掌握2个就能够了。一个参数为name由于JAP在映射属性到数据库时,若是没有指定@Column参数,则默认使用和实体类中的属性同样的名字,若是指定了@Column则使用该注解中的name参数。第二个参数为columnDefinition参数,该参数则是能够直接将咱们建立表中的语句写在该参数中,这样咱们能够很方便的控制字段的长度及类型。还有一个注解为@indexes。该注解可指定咱们指定属性为表中的索引,这里要注意一下若是表中字段名字和实体类中的属性名字不一致,@indexes注解须要指定的是实体类中的属性名,则不是真正表中的字段名。下面咱们启动项目,看一下数据库中的表结构是否是和咱们实体类中映射的同样。。
咱们如今看数据库中的映射,除了建立索引时自动生成的索引名不同,其它的字段映射类型长度及其描述都和咱们实体类中的一致。
下面咱们介绍一下怎么使用JPA来操做数据库。咱们以增删改查为例,分别介绍它们的使用。在使用JAP操做数据库时,咱们须要建立一个和实体类相对应的接口,而且让该接口继承JAP中已经提供的JpaRepository(有不少个接口暂时只介绍这一个)接口。这样咱们就能够经过这个接口来操做数据库了。下面咱们看一下该接口的源码。
package com.jilinwula.springboot.helloworld.Repository; import com.jilinwula.springboot.helloworld.entity.UserInfoEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserInfoRepository extends JpaRepository<UserInfoEntity, Long> { }
在咱们继承JpaRepository接口时须要咱们指定两个参数,第一个参数表示咱们要操做的实体类是哪个,第二个参数表示咱们实体类中的主键类型,其次咱们还须要添加@Repository注解,这样JPA才能操做数据库。下面咱们建立一个测试用例,分别介绍数据库中的增删改查。下面为测试用例源码:
package com.jilinwula.springboot.helloworld; import com.jilinwula.springboot.helloworld.Repository.UserInfoRepository; import com.jilinwula.springboot.helloworld.entity.UserInfoEntity; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JilinwulaSpringbootHelloworldApplicationTests { @Autowired private UserInfoRepository userInfoRepository; @Test public void save() { UserInfoEntity userInfoEntity = new UserInfoEntity(); userInfoEntity.setUsername("吉林乌拉"); userInfoEntity.setPassword("jilinwula"); userInfoEntity.setNickname("二十四分之七倍根号六"); userInfoEntity.setRoleId(1L); userInfoRepository.save(userInfoEntity); } @Test public void contextLoads() { } }
咱们暂时只写一个新增的方法,而且咱们发现,虽然咱们的UserInfoRepository接口中没有写任何方法,但咱们竟然能够直接调用save方法了。这是由于当咱们将UserInfoRepository接口继承JpaRepository接口时,是默认继承了该接口的一些方法,因此这些基本的增删改查操做,是不须要咱们写任何代码的。下面咱们执行一下测试用例,看看该条数据可否正确的插入到数据库中。
咱们看数据已经成功的添加到了数据库中。下面咱们在添加一条,方便咱们之后测试。
。下面咱们编写一下查询语句,看看可否正确查出数据。
@Test public void select() { UserInfoEntity userInfoEntity = userInfoRepository.findOne(1L); System.out.println(userInfoEntity); }
咱们看一样,咱们仍是没有写findOne方法,可是咱们竟然能够直接使用。findOne方法是JPA为咱们提供经过主键查询数据的方法,因此该方法的返回值是实体类对象,由于只能返回一条数据。下面咱们执行一下该测试用例,看看可否正确查询出数据。
UserInfoEntity(id=1, username=吉林乌拉, password=jilinwula, nickname=二十四分之七倍根号六, roleId=1)
咱们看已经成功的查询出数据了。这时有人会说,若是咱们想查全部的数据应该怎么办呢?别着急,JPA中除了提供了findOne方法,还提供了findAll方法,顾名思义,该方法就是查询全部数据的。既然是全部数据,因此该方法的返回值为List。下面为测试用例源码,及其执行日志。
@Test public void selectAll() { List<UserInfoEntity> userInfoEntitys = userInfoRepository.findAll(); System.out.println(userInfoEntitys); }
[UserInfoEntity(id=1, username=吉林乌拉, password=jilinwula, nickname=二十四分之七倍根号六, roleId=1), UserInfoEntity(id=2, username=阿里巴巴, password=alibaba, nickname=淘宝, roleId=2)]
下面咱们介绍一下更新方法,在JPA中更新方法和save方法同样,惟一的区别就是若是咱们在实体类中设置了主键,则调用sava方法时,JPA执行的就是更新。若是不设置主键,则JPA执行的就是新增。下面为测试用例源码:
@Test public void update() { UserInfoEntity userInfoEntity = new UserInfoEntity(); userInfoEntity.setId(1L); userInfoEntity.setUsername("阿里巴巴"); userInfoEntity.setPassword("alibaba"); userInfoEntity.setNickname("淘宝"); userInfoEntity.setRoleId(2L); userInfoRepository.save(userInfoEntity); }
如今咱们在查询一下数据库,若是更新语句成功,那么此时数据库中则会有两条同样的数据。
咱们看,数据库中的确有两条如出一辙的数据了,这就证实了咱们刚刚的更新语句成功了。下面咱们介绍一下最后一个删除语句,该语句也一样比较简单,由于JPA也一样为咱们提供了该方法,下面为测试用例。
@Test public void delete() { userInfoRepository.delete(1L); }
咱们在查询一下数据库,看看id为1的数据是否还在数据库中存在。
咱们看该数据成功的删除了。这就是JPA对数据库的增删改查的基本操做。固然JPA中还提供了不少复杂的语法,例如级联查询、分页查询等等。这些高级的功能咱们在后续的文章中在作详细介绍。这就是本篇的所有内容,若有疑问,欢迎留言,谢谢。
下面为项目源码:https://github.com/jilinwula/jilinwula-springboot-helloworld3