知識點java
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis避免了幾乎全部的JDBC代碼和手動設置參數以及獲取結果集。MyBatis可使用簡單的XML或註解來配置和映射原生信息,將接口和Java的POJOs(Plain Ordinary Java Object,普通的 Java對象)映射成數據庫中的記錄。mysql
其它持久層解決方案:JDBC、DBUtils、JdbcTemplate、Hibernatesql
1)新建數據庫表:user數據庫
CREATE TABLE `user` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼
2)新建pojo實體類:User編程
public class User implements Serializable {
private static final long serialVersionUID = -6611101295813775324L;
private Integer id;
private String name;
private Date createTime;
private Long updateTime;
//省略setter、getter方法
}
複製代碼
3)配置數據庫鏈接屬性:jdbc.properties緩存
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=root
default.environment=dev
複製代碼
4)配置mybatis配置文件:mybatis-config.xml安全
<?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="jdbc.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="${default.environment}">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers
<mapper resource="mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
複製代碼
5)自定義類型處理器:CustomHandlerbash
@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(Long.class)
public class CustomHandler extends BaseTypeHandler<Long> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
//ps.setDate(i,new Date(parameter));
ps.setTimestamp(i,new Timestamp(parameter));
}
@Override
public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getDate(columnName).getTime();
}
@Override
public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getDate(columnIndex).getTime();
}
@Override
public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getDate(columnIndex).getTime();
}
}
複製代碼
6)配置mapper.xml文件:UserMapper.xmlsession
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cyan.mapper.UserMapper">
<resultMap id="baseResultMap" type="com.cyan.pojo.User">
<id property="id" column="id" jdbcType="INTEGER"></id>
<result property="name" column="name" jdbcType="VARCHAR"></result>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"></result>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP" typeHandler="com.cyan.handler.CustomHandler"></result>
</resultMap>
<select id="selectUserById" parameterType="java.lang.Integer" resultMap="baseResultMap">
select * from user where id = #{id}
</select>
</mapper>
複製代碼
7)編寫測試用例:UserTestmybatis
public class UserTest {
private SqlSession session;
@Before
public void init()throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession(true);
}
@Test
public void test01() throws IOException {
User result = session.selectOne("com.cyan.mapper.UserMapper.selectUserById", 1);
System.out.println(result.toString());
}
}
複製代碼
SqlSessionFactoryBuilder:用於構建會話工廠,基於mybatis-config.xml中的environment 、props 構建會話工廠,構建完成後便可丟棄。
SqlSessionFactory:用於生成會話的工廠,做用於整個應用運行期間,通常不須要構造多個工廠對象
SqlSession:做用於單次會話,如WEB一次請求期間,不能用做於某個對像屬性,也不能在多個線程間共享,由於它是線程不安全的。
因爲每次調用時都去找對應用statement以及拼裝參數,使用上不是特別友好
myBatis引入了接口的機制,將接口與mapper.xml 的namespace名稱綁定,MyBatis就能夠根據ASM工具動態構建該接口的實例。
1)建立mapper接口:UserMapper
User selectUser(Integer id);
複製代碼
2)配置mapper.xml文件:UserMapper.xml
<select id="selectUser" resultType="com.tuling.ssm.pojo.User">
select * from user where id = #{id}
</select>
複製代碼
3)編寫測試用例:UserTest
UserMapper $proxy = session.getMapper(UserMapper.class);
User user = $proxy.selectUserById(2);
System.out.println(user.toString());
複製代碼
知識點
properties元素能夠經過resource或url加載外部properties文件中的屬性,也能夠直接設置property屬性。而後在xml中就能夠經過${屬性名}進行引用替換。
<properties resource="jdbc.properties"></properties>
複製代碼
引用屬性方式
${jdbc.driverClassName}
# MyBatis3.4.2開始,支持指定默認值
${jdbc.driverClassName:com.mysql.jdbc.Driver}
複製代碼
一個項目常常須要在例如開發壞境、測試環境、預上線環境、生產環境中等不一樣環境中進行部署,每一個環境所對應的參數是不同的,myBatis 中能夠經過 environment 來設置不一樣環境的屬性。
# 指定應用環境
default.environment=dev
複製代碼
<environments default="${default.environment}">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
複製代碼
設置MyBatis 全局參數,約定myBatis的全局行爲
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
複製代碼
在myBatis中常常會用到java中類型,如sql塊中的參數集中javaType、結果集映射中的javaType等 ,都要使用java 全路徑名,能夠經過typeAliases屬性設置別名
<typeAliases>
<!--<typeAlias type="com.cyan.pojo.User" alias="User"></typeAlias>-->
<package name="com.cyan.pojo"></package>
</typeAliases>
複製代碼
持久層框架其中比較重要的工做就是處理數據的映射轉換,把java類型轉換成jdbc類型的參數,又須要把jdbc類型的結果集轉換成java類型。在mybatis中是經過TypeHandler接口來實現的。
自定義類型處理器
public class CustomHandler extends BaseTypeHandler<Long> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
//ps.setDate(i,new Date(parameter));
ps.setTimestamp(i,new Timestamp(parameter));
}
@Override
public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getDate(columnName).getTime();
}
@Override
public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getDate(columnIndex).getTime();
}
@Override
public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getDate(columnIndex).getTime();
}
}
複製代碼
配置文件方式應用
<typeHandlers>
<typeHandler handler="com.cyan.handler.CustomHandler"
javaType="long" jdbcType="TIMESTAMP" />
</typeHandlers>
複製代碼
註解方式應用
@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(Long.class)
public class CustomHandler extends BaseTypeHandler<Long> {}
複製代碼
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/AccountMapper.xml"></mapper>
<!-- mapper與對應的xml文件必須在一個包下 -->
<!--<mapper class="com.cyan.mapper.UserMapper"/>-->
<!--<package name="com.cyan.mapper"/>-->
</mappers>
複製代碼
resource:基於classpath加載xml文件
class:基於接口加載
package:掃描包下全部class,而後進行加載
約定規則:
1)mapper中的namespace必須與對應的接口名稱對應
2)經過class或package加載時,xml文件必須與接口在同一級目錄
知識點
1)Mapper中的常見元素
resultMap – 結果集映射
select – 查詢語句
insert – 插入語句
cache – 對給定命名空間的緩存配置
parameterMap - 參數集映射
update – 更新語句
delete – 刪除語句
cache-ref - 指定緩存命名空間
sql – 可被其餘語句引用的可重用語句塊。
複製代碼
2)select中的常見屬性
id - 語句塊的惟一標識,與接口中方法名稱對應
parameterMap - 參數集映射
parameterType - 參數java類型
resultMap - 返回結果映射
resultType - 返回結果java類型
statementType - 預處理類型
timeout - 超時時間
flushCache - 每次調用都會刷新一二級緩存
useCache - 是否保存至二級緩存當中去
複製代碼
3)insert&update&delete中的常見屬性
id - 語句塊的惟一標識,與接口中方法名稱對應
parameterMap - 參數集映射
parameterType - 參數java類型
statementType - 預處理類型
timeout - 超時時間
flushCache- true每次調用都會刷新一二級緩存
# insert、update還具備以下三個屬性(delete則沒有)
keyProperty - 主鍵對應的java屬性,多個用 逗號分割
keyColumn - 主鍵列,多個用逗號分割
useGeneratedKeys - 插入成功後能夠獲取到數據庫自動生成的主鍵值
複製代碼
參數映射是最強大功能之一,基本能夠經過如下方式進行引用
1)單個簡單參數引用:若是方法中只有一個參數可經過任意名稱進行引用
User selectUserById(Integer id);
<select id="selectUserById" parameterType="java.lang.Integer" resultMap="baseResultMap">
select * from user where id = #{id}
</select>
複製代碼
2)多個簡單參數引用:經過參數下標引用#{param1},#{param2}
Integer insertUser(String name, Date createTime, Date updateTime);
<insert id="insertUser">
insert into user(name,create_time,update_time) values (#{param1},#{param2},#{param3})
</insert>
複製代碼
3)對象屬性引用:直接經過對象屬性名稱引用,嵌套對象經過.進行引用
Integer saveUser(User user);
<insert id="saveUser" parameterType="com.cyan.pojo.User"
keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into user(name,create_time,update_time) values (#{name},#{createTime},#{updateTime,typeHandler=com.cyan.handler.CustomHandler})
</insert>
複製代碼
4)map key值引用
<update id="updateUserById" parameterType="java.util.Map">
update user set name = #{name},update_time = #{updateTime} where id = #{id}
</update>
複製代碼
5)變量名引用
Integer modifyUserById(@Param("id") Integer id,@Param("name") String name,@Param("updateTime")Date updateTime);
<update id="modifyUserById">
update user set name = #{name},update_time = #{updateTime} where id = #{id}
</update>
複製代碼
6)參數拼接
基於#的參數引用,其原理是經過?佔位符進行預處理能得到更好的性能和安全性(防止SQL注入),但有些需求是經過?佔位沒法實現的,好比在一些分庫分表的場景中咱們須要動態的拼接表結構。好比某系統日誌表是按年進行切割的2018_systemlog,2019_systemlog這時就能夠經過以下語句進行
@Select("select * from ${year}_user where id = #{id}")
User selectUserTableByYear(String year, Integer id);
複製代碼
1)結果集自動映射
在select中指定resultType=""後無須要任何配置 myBatis會基於resultType中的java類型及屬性自動推斷生成一個隱示的resultMap,從而完成結果映射
複製代碼
2)resultMap
有時jdbc並非與java Bean徹底貼合這時就須要手動設置resultMap
<resultMap id="baseResultMap" type="com.cyan.pojo.User">
<id property="id" column="id" jdbcType="INTEGER"></id>
<result property="name" column="name" jdbcType="VARCHAR"></result>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"></result>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP" typeHandler="com.cyan.handler.CustomHandler"></result>
</resultMap>
<select id="selectUserById" parameterType="java.lang.Integer" resultMap="baseResultMap">
select * from user where id = #{id}
</select>
ID:用於結果集中的惟一標識
result:設置一個某經過字段
property:java屬性名
jdbcType:jdbc類型
javaType:java類型
column:數據庫列名
typeHandler:類型處理器
複製代碼
3)嵌套結果映射(一對一)
建立相關數據庫:
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`money` int(11) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼
AccountMapper.xml:
<resultMap id="accountAndUserResultMap" type="com.cyan.pojo.Account">
<id property="id" column="id"/>
<association property="user" javaType="com.cyan.pojo.User">
<id property="id" column="user_id"/>
<result property="name" column="userName"/>
</association>
</resultMap>
<select id="getAccountList" resultMap="accountAndUserResultMap">
SELECT a.*, u.name userName from account a,user u where a.user_id=u.id
</select>
複製代碼
4)引入外部select(一對一)
AccountMapper.xml:
<resultMap id="accountAmpUserResultMap" type="com.cyan.pojo.Account">
<id property="id" column="id"/>
<association property="user" javaType="com.cyan.pojo.User"
select="com.cyan.mapper.UserMapper.selectUserById" column="user_id">
</association>
</resultMap>
<select id="selectAccountList" resultMap="accountAmpUserResultMap">
SELECT a.*, u.name userName from account a,user u where a.user_id=u.id
</select>
複製代碼
5)嵌套結果映射(1對多)
建立相關數據庫:
CREATE TABLE teacher(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(20)
);
INSERT INTO teacher(t_name) VALUES('LS1');
INSERT INTO teacher(t_name) VALUES('LS2');
CREATE TABLE class(
c_id INT PRIMARY KEY AUTO_INCREMENT,
c_name VARCHAR(20),
teacher_id INT
);
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);
INSERT INTO class(c_name, teacher_id) VALUES('bj_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('bj_b', 2);
CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
INSERT INTO student(s_name, class_id) VALUES('xs_A', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_B', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_C', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_D', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_E', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_F', 2);
複製代碼
ClassesMapper.xml:
<!-- 根據 classId 查詢對應的班級信息,包括學生,老師 -->
<!-- 方式一:嵌套結果(使用嵌套結果映射來處理重複的聯合結果的子集) -->
<resultMap id="baseResultMap1" type="com.cyan.pojo.Classes">
<id property="id" column="c_id" />
<id property="name" column="c_name" />
<association property="teacher" column="teacher_id" javaType="com.cyan.pojo.Teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
</association>
<collection property="students" column="c_id" ofType="com.cyan.pojo.Student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
</collection>
</resultMap>
<select id="getClassesById" parameterType="int" resultMap="baseResultMap1">
select * from class c,teacher t, student s
where c.teacher_id = t.t_id
and c.c_id = s.class_id
and c.c_id = #{id}
</select>
複製代碼
6)引入外部select(一對多)
ClassesMapper.xml:
<!-- 根據 classId 查詢對應的班級信息,包括學生,老師 -->
<!-- 方式二:嵌套查詢(經過執行另一個SQL映射語句來返回預期的複雜類型) -->
<resultMap id="baseResultMap2" type="com.cyan.pojo.Classes">
<id property="id" column="c_id" />
<id property="name" column="c_name" />
<association property="teacher" column="teacher_id" javaType="com.cyan.pojo.Teacher"
select="com.cyan.mapper.TeacherMapper.getTeacherById" />
<collection property="students" column="c_id" ofType="com.cyan.pojo.Student"
select="com.cyan.mapper.StudentMapper.getStudentById"/>
</resultMap>
<select id="findClassesById" parameterType="int" resultMap="baseResultMap2">
select * from class where c_id = #{id}
</select>
複製代碼
TeacherMapper.xml:
<resultMap id="baseResultMap" type="com.cyan.pojo.Teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
</resultMap>
<select id="getTeacherById" resultMap="baseResultMap" parameterType="int">
select * from teacher where t_id = #{id}
</select>
複製代碼
StudentMapper.xml:
<resultMap id="baseResultMap" type="com.cyan.pojo.Student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
</resultMap>
<select id="getStudentById" resultMap="baseResultMap" parameterType="int">
select * from student where class_id = #{id}
</select>
複製代碼