前面的鋪墊工做,目前已經作得差很少了,接下來咱們將要學習有關Spring boot的數據訪問技術,包括:jdbc技術、MyBatis、Spring Data JPA,他着眼於整個JAVAEE。java
對於數據訪問層,不管是SQL仍是NOSQL,springboot默認採用整合Spring Data的方式進行統一處理,添加大量自動配置,屏蔽了不少設置。引入各類xxxTemplate、xxxResitory來簡化咱們對數據訪問層的操做。對咱們來講只須要進行簡單的設置便可。咱們將在接下來的學習過程當中學到SQL相關、NOSQL在緩存、消息、檢索等章節相關的內容。mysql
Spring Data相關文檔:前往web
經過springboot initializer建立一個選擇了web、jdbc、mysql幾個模塊。spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
能夠看到,引入了一個jdbc的場景啓動器,以及底層依賴mysql-connector-java,而且是運行時依賴runtime
。sql
在此以前,請注意上一節有關docker的內容,安裝好了mysql服務,個人地址是10.21.1.47:3306,其帳號密碼root:123456。若是您也在測試的話,請保證本身安裝了mysql服務,並正常啓動。docker
還要記得在mysql中建立一個本身的數據庫,我這裏建立了一個數據庫,名爲joyblack.數據庫
咱們在配置文件中配置鏈接數據庫的配置:json
server: port: 8086 spring: datasource: username: root password: 123456 url: jdbc:mysql://10.21.1.47:3306/joyblack?characterEncoding=utf8&serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver initialization-mode: always
@RunWith(SpringRunner.class) @SpringBootTest public class JdbcwebApplicationTests { @Autowired DataSource dataSource; @Test public void contextLoads() throws SQLException { System.out.println(dataSource.getClass()); System.out.println(dataSource.getConnection()); } }
com.mysql.jdbc.Driver 是 mysql-connector-java 5中的,com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6中的。數組
結果:緩存
com.zaxxer.hikari.HikariDataSource
做爲數據源,這是2.x版本哦,請留意。因爲視頻這裏是基於1.x版本的,有興趣的同窗就去參考一下,其實配置原理和以前講的大同小異,不過2.0在某些地方則須要進一步研究,其引進了很多java8新特性。這裏就不在闡述了。
咱們觀察源碼:
/** * Initialize a {@link DataSource} based on a matching {@link DataSourceProperties} * config. */ class DataSourceInitializer { private static final Log logger = LogFactory.getLog(DataSourceInitializer.class); private final DataSource dataSource; private final DataSourceProperties properties; private final ResourceLoader resourceLoader; /** * Create a new instance with the {@link DataSource} to initialize and its matching * {@link DataSourceProperties configuration}. * @param dataSource the datasource to initialize * @param properties the matching configuration * @param resourceLoader the resource loader to use (can be null) */ DataSourceInitializer(DataSource dataSource, DataSourceProperties properties, ResourceLoader resourceLoader) { this.dataSource = dataSource; this.properties = properties; this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(); } /** * Create a new instance with the {@link DataSource} to initialize and its matching * {@link DataSourceProperties configuration}. * @param dataSource the datasource to initialize * @param properties the matching configuration */ DataSourceInitializer(DataSource dataSource, DataSourceProperties properties) { this(dataSource, properties, null); } public DataSource getDataSource() { return this.dataSource; } /** * Create the schema if necessary. * @return {@code true} if the schema was created * @see DataSourceProperties#getSchema() */ public boolean createSchema() { List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema"); if (!scripts.isEmpty()) { if (!isEnabled()) { logger.debug("Initialization disabled (not running DDL scripts)"); return false; } String username = this.properties.getSchemaUsername(); String password = this.properties.getSchemaPassword(); runScripts(scripts, username, password); } return !scripts.isEmpty(); } /** * Initialize the schema if necessary. * @see DataSourceProperties#getData() */ public void initSchema() { List<Resource> scripts = getScripts("spring.datasource.data", this.properties.getData(), "data"); if (!scripts.isEmpty()) { if (!isEnabled()) { logger.debug("Initialization disabled (not running data scripts)"); return; } String username = this.properties.getDataUsername(); String password = this.properties.getDataPassword(); runScripts(scripts, username, password); } } private boolean isEnabled() { DataSourceInitializationMode mode = this.properties.getInitializationMode(); if (mode == DataSourceInitializationMode.NEVER) { return false; } if (mode == DataSourceInitializationMode.EMBEDDED && !isEmbedded()) { return false; } return true; } private boolean isEmbedded() { try { return EmbeddedDatabaseConnection.isEmbedded(this.dataSource); } catch (Exception ex) { logger.debug("Could not determine if datasource is embedded", ex); return false; } } private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) { if (resources != null) { return getResources(propertyName, resources, true); } String platform = this.properties.getPlatform(); List<String> fallbackResources = new ArrayList<>(); fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); fallbackResources.add("classpath*:" + fallback + ".sql"); return getResources(propertyName, fallbackResources, false); } private List<Resource> getResources(String propertyName, List<String> locations, boolean validate) { List<Resource> resources = new ArrayList<>(); for (String location : locations) { for (Resource resource : doGetResources(location)) { if (resource.exists()) { resources.add(resource); } else if (validate) { throw new InvalidConfigurationPropertyValueException(propertyName, resource, "The specified resource does not exist."); } } } return resources; } private Resource[] doGetResources(String location) { try { SortedResourcesFactoryBean factory = new SortedResourcesFactoryBean( this.resourceLoader, Collections.singletonList(location)); factory.afterPropertiesSet(); return factory.getObject(); } catch (Exception ex) { throw new IllegalStateException("Unable to load resources from " + location, ex); } } private void runScripts(List<Resource> resources, String username, String password) { if (resources.isEmpty()) { return; } ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.setContinueOnError(this.properties.isContinueOnError()); populator.setSeparator(this.properties.getSeparator()); if (this.properties.getSqlScriptEncoding() != null) { populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name()); } for (Resource resource : resources) { populator.addScript(resource); } DataSource dataSource = this.dataSource; if (StringUtils.hasText(username) && StringUtils.hasText(password)) { dataSource = DataSourceBuilder.create(this.properties.getClassLoader()) .driverClassName(this.properties.determineDriverClassName()) .url(this.properties.determineUrl()).username(username) .password(password).build(); } DatabasePopulatorUtils.execute(populator, dataSource); } }
不難發現:
createSchema();
運行建表語句;initSchema();
運行插入數據的sql語句;同時,默認只須要將文件命名爲:
schema-*.sql、data-*.sql 默認規則:schema.sql,schema-all.sql; 可使用 schema: - classpath:department.sql 指定位置
咱們建立一個建表文件
/* Navicat MySQL Data Transfer Source Server : docker Source Server Version : 50505 Source Host : 10.21.1.47:3306 Source Database : joyblack Target Server Type : MYSQL Target Server Version : 50505 File Encoding : 65001 Date: 2018-12-19 14:03:49 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `user_name` varchar(20) NOT NULL, `login_name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ----------------------------
將其放在資源目錄下便可。運行項目,能夠看到生成了具體的數據庫表user
;
接下來,咱們放置初始化數據表的文件
INSERT INTO `user` VALUES ('1', '阿庫婭', 'akuya'); INSERT INTO `user` VALUES ('2', '克里斯汀娜', 'cristiner'); INSERT INTO `user` VALUES ('3', '惠惠', 'huihui');
一樣將其放在資源目錄下便可。運行項目,能夠看到生成了具體的數據庫表user
的3條用戶數據(爲美好的明天獻上祝福的女主角們);
sql-script-encoding: utf-8
在配置文件中配置此設置,保證中文不亂碼,若是還發現亂碼,請注意本身的鏈接url是否配置了characterEncoding=utf8
;
若報出
The server time zone value '???ú±ê×??±??' is unrecognized...
這樣的錯誤,請注意本身的鏈接url是否配置了serverTimeZone=GMT
;
2.x版本的springboot必須指定
initialization-mode: always
配置,每次重啓生成腳本纔會執行;
另外,咱們能夠從源碼中看到,該文件是能夠配置位置的
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) { if (resources != null) { return this.getResources(propertyName, resources, true); } else { String platform = this.properties.getPlatform(); List<String> fallbackResources = new ArrayList(); fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); fallbackResources.add("classpath*:" + fallback + ".sql"); return this.getResources(propertyName, fallbackResources, false); } }
若是咱們配置了this.properties.getData()
,則springboot會從該位置獲取初始化文件,而該值根據咱們前面講過的模式,能夠繼續追溯,就是
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { ... private List<String> schema; ...
也就是說,咱們只須要配置一個spring.datasource.schema
的數組對象,指明這些初始化文件的位置就能夠了。
同理,另一個則是spring.datasource.data
,配置這兩個參數就對應咱們上述的兩種文件了(初始化表結構;初始化表數據)。
咱們能夠隨便測試一下:
server: port: 8086 spring: datasource: username: root password: 123456 url: jdbc:mysql://10.21.1.47:3306/joyblack?characterEncoding=utf8&serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver initialization-mode: always sql-script-encoding: utf-8 schema: - classpath:schema1.sql data: - classpath:data1.sql
在此以前,記得複製兩個文件爲xx1.sql。
這裏觀察源碼還發現兩個操做方法都調用了
runScript
,也就是說,其底層都是同樣的執行腳本方式,咱們大能夠將初始化表結構和添加數據的腳本混合成一個,可是可讀性差了一些,固然也更加的方便管理,利弊見仁見智了。
這時候,咱們就能夠直接操做數據庫的數據了,這裏簡單的演示一下查詢操做,其餘的操做也差很少是這樣的模式。
咱們建立一個controller查詢數據
@RestController public class IndexController { @Autowired private JdbcTemplate jdbcTemplate; @GetMapping("/query") public String index(){ jdbcTemplate.execute("delete from user where id = 1"); List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from user"); return maps.toString(); } }
運行項目以後咱們會發現數據庫id爲1的user表數據被刪除,同事訪問網站首頁,能夠獲得以下的返回:
[{id=2, user_name=克里斯汀娜, login_name=cristiner}, {id=3, user_name=惠惠, login_name=huihui}]