一、物理外鍵
1.1 外鍵的理解
外鍵是建立在從表的一個字段(通常是專門新建的字段)指向主表的一個字段(通常是被設爲主鍵的一個字段)的引用,用於強調和約束兩個表的主從關係。
1.2 外鍵的選擇
一對一
一般一對一關係的表存在明顯的主從關係,一般在從表中新建一個用於保存關係的字段,並建立由字段指向主表主鍵的外鍵。
一對多、多對一
一般在一對多、多對一關係的表中,一般在表示多關係的表中新建用於保存關係的字段,並建立由該字段指向表示一關係的表主鍵的外鍵。
多對多
一般需要新建一張中間表用於保存關係,有兩個字段並建立分別由這兩個字段指向表示關係多的表主鍵的外鍵,並且一般設這兩個字段爲主鍵(聯合主鍵)。
1.3 外鍵優缺點
外鍵的存在保證了數據的強一致性,表關係一目瞭然而且級聯操作非常方便。但是在INSERT
、UPDATE
、DELETE
等操作時等都會先檢查外鍵的約束條件再操作,性能有所下降。
阿里巴巴的MySQL規約中強制禁用物理外鍵。因此通常情況下都用中間表來存儲表的關係,由應用層維護數據的一致性。
【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。 說明:以學生和成績的關係爲例,學生表中的student_id是主鍵,那麼成績表中的student_id則爲外鍵。如果更新學生表中的student_id,同時觸發成績表中的student_id更新,即爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。
二、級聯查詢
2.1 示例
2.2 JOIN
連接 | 說明 |
---|---|
INNER JOIN (內連接) |
只匹配兩個表的交集 |
LEFT OUTER JOIN (左外連接) |
儘可能的匹配左表 |
RIGHT OUTER JOIN (右外連接) |
儘可能的匹配右表 |
FULL OUTER JOIN (全連接) |
全部匹配 |
由於MySQL不支持FULL JOIN
,可以用UNION
來實現全連接的功能
三、代碼生成器(MyBatis Generator)
在IDEA上有着豐富的代碼自動生成插件,基本上可以生成絕大部分的
domain
、dao
、service
、controller
層的代碼。比如Free MyBatis Plugin自帶的generator、Mybatis Generator(IDEA插件名)、Easy Code、Code Maker等等。
3.1 依賴
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
3.2 數據庫連接配置db.properties
MySQL如果沒配置時區,可能需要在url
後追加時區serverTimezone=UTC
。配置方法在MySQL——MySQL8時區問題這篇博客裏
db.driver-location=E:/repository/mysql/mysql-connector-java/8.0.16/mysql-connector-java-8.0.16.jar
db.driver-class-name=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/demo-test
db.username=root
db.password=root
3.3 生成器配置generatorConfig.xml
通常需要配置的地方有6處:
- 添加
context
節點以配置多數據源。 - 添加
plugin
節點以配置插件。 - 修改
targetPackage
屬性以修改生成文件的路徑。 - 修改
javaClientGenerator
節點的type
屬性以ANNOTATEDMAPPER
(註解)、XMLMAPPER
(xml文件)、MIXEDMAPPER
(混合)實現SQL語句。 - 添加/修改
table
節點的tableName
和domainObjectName
以配置表到實體的映射。 - 添加/修改
table/ignoreColumn
節點以配置需要忽略的從表到實體的映射。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="db.properties"/>
<classPathEntry location="${db.driver-location}"/>
<context id="default" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="${db.driver-class-name}"
connectionURL="${db.url}"
userId="${db.username}"
password="${db.password}">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="model.generator" targetProject="./src/main/java">
<property name="enableSubPackages" value="false"/>
<property name="constructorBased" value="true"/>
<property name="trimStrings" value="true"/>
<property name="immutable" value="false"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="mapper.generator" targetProject="./src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="dao.generator" targetProject="./src/main/java">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<table tableName="t_type" domainObjectName="Type"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table tableName="t_hero" domainObjectName="Hero"
enableCountByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
enableUpdateByExample="false">
<ignoreColumn column="_type_name"/>
</table>
<table tableName="t_player" domainObjectName="Player"
enableCountByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
enableUpdateByExample="false">
</table>
</context>
</generatorConfiguration>
3.4 配置以Maven插件的方式運行
在build/plugins/
節點下添加以下插件:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
</plugin>
默認會從classpath
路徑下讀取generatorConfig.xml
爲配置文件,通常不需要配置。
classpath
是指項目編譯後的根目錄,在Maven的項目目錄中默認爲./src/main/java/
和./src/main/resources/
這兩個目錄
配置完成後的默認命令爲mybatis-generator:generate
,並且重複執行並不會覆蓋舊文件。MyBatis Generator通常需要數據庫連接驅動的全限定名。
<classPathEntry location="${db.driver-location}"/>
不過也可以使用Maven的依賴:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</plugin>
四、CRUD
4.1 實體關係
4.2 xml文件
查詢通常需要自定義結果映射
resultMap
,其中id
表示主鍵字段映射關係,result
表示普通字段的映射關係,一般只需配置property
屬性(實體中的字段)和column
屬性(表中的字段)。級聯查詢時
collection
用於配置與表示多關係的實體的映射,以ofType
屬性指定集合中的元素類型`。association
用於配置與表示一關係的實體的映射,以javaType
屬性指定字段類型。
4.2.1 TypeMapper.xml
4.2.2 HeroMapper.xml
4.2.3 PlayerMapper.xml
4.3 配置文件mybatis-config.xml
通常配置文件都是直接放在
classpath
路徑下的方便讀取。另外如果不把MyBatis交給Spring
管理的話,貌似mappers/mapper
節點不支持通配符。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver-class-name}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/generator/TypeMapper.xml"/>
<mapper resource="mapper/generator/HeroMapper.xml"/>
<mapper resource="mapper/generator/PlayerMapper.xml"/>
</mappers>
</configuration>
4.4 測試類App.java
public class App {
public static void main(String[] args) {
SqlSession sqlSession = getSqlSession();
/*
HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class);
Hero h = heroMapper.selectByCascade("花木蘭");
System.out.println(h);
PlayerMapper playerMapper = sqlSession.getMapper(PlayerMapper.class);
Player p = playerMapper.selectByCascade("教主");
System.out.println(p);
*/
TypeMapper typeMapper = sqlSession.getMapper(TypeMapper.class);
Type t = typeMapper.selectByCascade("刺客");
System.out.println(t);
sqlSession.close();
}
public static SqlSession getSqlSession() {
String resource = "mybatis-config.xml";
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
return null;
}
}
4.4 IDEA添加toString()
的JSON模板
ALT+INSERT打開toString()
的模板設置,添加以下模板並應用
public java.lang.String toString() {
final java.lang.StringBuilder sb = new java.lang.StringBuilder("{");
#set ($i = 0)
#foreach ($member in $members)#if ($i == 0)
sb.append("#####
#else
sb.append(",####
#end#if ($member.string || $member.date)
\"$member.name\":\"")
#else
\"$member.name\":")
#end#if ($member.primitiveArray || $member.objectArray)
.append(java.util.Arrays.toString($member.name));
#elseif ($member.string || $member.date)
.append($member.accessor).append('\"');
#else
.append($member.accessor);
#end#set ($i = $i + 1)
#end
sb.append('}');
return sb.toString();
}
4.5 測試輸出
複製輸出到JSON中格式化