上一篇中咱們已經介紹了在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