爲什麼使用MyBatis
MyBatis是一個半自動化的持久化層框架。
• JDBC
– SQL夾在Java代碼塊裏,耦合度高導致硬編碼內傷
– 維護不易且實際開發需求中sql是有變化,頻繁修改的情況多見 • Hibernate和JPA
– 長難複雜SQL,對於Hibernate而言處理也不容易
– 內部自動生產的SQL,不容易做特殊優化。
– 基於全映射的全自動框架,大量字段的POJO進行部分映射時比較困難。
導致數據庫性能下降。
• 對開發人員而言,核心sql還是需要自己優化
• sql和java編碼分開,功能邊界清晰,一個專注業務、一個專注數據。
搭建環境之MyBatis操作數據庫
詳細教程
myBatis架包
看mybatis打印的SQL語句架包以及.xml文件
java.bean
package bao;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [password=" + password + ", username=" + username + "]";
}
public User() {
super();
}
}
創建MyBatis全局配置文件mybatis-config.xml
– MyBatis 的全局配置文件包含了影響 MyBatis 行爲甚深
的設置(settings)和屬性(properties)信息、如數據
庫連接池信息等。指導着MyBatis進行工作。我們可以
參照官方文件的配置示例。
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost:1527/myeclipse" />
<property name="username" value="classiccars" />
<property name="password" value="myeclipse" />
</dataSource>
</environment>
</environments>
<!-- 將我們寫好的sql映射文件(EmployeeMapper.xml)一定要註冊到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
創建SQL映射文件 EmployeeMapper.xml文件
– 映射文件的作用就相當於是定義Dao接口的實現類如何
工作。這也是我們使用MyBatis時編寫的最多的文件。
<?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">
<!--
namespace:名稱空間;指定爲接口的全類名
id:唯一標識
resultType:返回值類型
#{id}:從傳遞過來的參數中取出id值
public Employee getEmpById(Integer id);
-->
<mapper namespace="dao">
<select id="selectUser" resultType="bao.User">
select * from USER1 where USERNAME = #{id}
</select>
</mapper>
測試類
1、根據全局配置文件,利用SqlSessionFactoryBuilder創建SqlSessionFactory
2、使用SqlSessionFactory獲取sqlSession對象。一個SqlSession對象代表和數據庫的一次會話
package bao;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
public class MyBatisTest {
/**
* 1、根據xml配置文件(全局配置文件)創建一個SqlSessionFactory對象 有數據源一些運行環境信息
* 2、sql映射文件;配置了每一個sql,以及sql的封裝規則等。
* 3、將sql映射文件註冊在全局配置文件中
* 4、寫代碼:
* 1)、根據全局配置文件得到SqlSessionFactory;
* 2)、使用sqlSession工廠,獲取到sqlSession對象使用他來執行增刪改查
* 一個sqlSession就是代表和數據庫的一次會話,用完關閉
* 3)、使用sql的唯一標誌來告訴MyBatis執行哪個sql。sql都是保存在sql映射文件中的。
*
* @throws IOException
*/
// 2、獲取sqlSession實例,能直接執行已經映射的sql語句
// sql的唯一標識:statement Unique identifier matching the statement to use.
// 執行sql要用的參數:parameter A parameter object to pass to the statement.
public static void main(String []args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try { System.out.println("segerh");
User blog = session.selectOne("dao.selectUser","df");
System.out.println(blog);
} finally {
session.close();
}
}
}
佈局
注意
• SqlSession 的實例不是線程安全的,因此是不能被共享的。
• SqlSession每次使用完成後需要正確關閉,這個關閉操作是必須的
• SqlSession可以直接調用方法的id進行數據庫操作,但是我們一般還是推薦使用SqlSession獲取到Dao接口的代理類,執行代理對象的方法,可以更安全的進行類型檢查操作
接口式編程
在以上基礎上做
先寫一個藉口
package bao;
public interface EmployeeMapper {
public User getEmpById(String id);
}
映射文件改變如下EmployeeMapper.xml
<?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="bao.EmployeeMapper">
<select id="getEmpById" resultType="bao.User">
select * from USER1 where USERNAME = #{id}
</select>
</mapper>
測試類
package bao;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
/**
* 1、接口式編程
* 原生: Dao ====> DaoImpl
* mybatis: Mapper ====> xxMapper.xml
*
* 2、SqlSession代表和數據庫的一次會話;用完必須關閉;
* 3、SqlSession和connection一樣她都是非線程安全。每次使用都應該去獲取新的對象。
* 4、mapper接口沒有實現類,但是mybatis會爲這個接口生成一個代理對象。
* (將接口和xml進行綁定)
* EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);
* 5、兩個重要的配置文件:
* mybatis的全局配置文件:包含數據庫連接池信息,事務管理器信息等...系統運行環境信息
* sql映射文件:保存了每一個sql語句的映射信息:
* 將sql抽取出來。
*
*
* @author lfy
*
*/
public class MyBatisTest {
public static void main(String []args) throws IOException {
// 1、獲取sqlSessionFactory對象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、獲取sqlSession對象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、獲取接口的實現類對象
//會爲接口自動的創建一個代理對象,代理對象去執行增刪改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
User employee = mapper.getEmpById("df");
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
}
MyBatis-全局配置文件
引入dtd約束
properties屬性
如果屬性在不只一個地方進行了配置,那麼 MyBatis 將按照下面的順序來加載:
– 在 properties 元素體內指定的屬性首先被讀取。
– 然後根據 properties 元素中的 resource 屬性讀取類路徑下屬性文件或根
據 url 屬性指定的路徑讀取屬性文件,並覆蓋已讀取的同名屬性。
– 最後讀取作爲方法參數傳遞的屬性,並覆蓋已讀取的同名屬性。
例子:
在同類下建一個.properties文件
全局配置文件如下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>
<!--若是再別的包下,/ 分割
1、mybatis可以使用properties來引入外部properties配置文件的內容;
resource:引入類路徑下的資源
url:引入網絡路徑或者磁盤路徑下的資源-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!-- 將我們寫好的sql映射文件(EmployeeMapper.xml)一定要註冊到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
settings設置
這是 MyBatis 中極爲重要的調整設置,它們會改變MyBatis 的運行時行爲。
例如:
例子:
在全局配置文件中 configuration內加入:
<!--
2、settings包含很多重要的設置項
setting:用來設置每一個設置項
name:設置項名
value:設置項取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases別名處理器
• 類型別名是爲 Java 類型設置一個短的名字,可以方便我們引用某個類。
• 類很多的情況下,可以批量設置別名這個包下的每一個類
創建一個默認的別名,就是簡單類名小寫。
• 也可以使用@Alias註解爲其指定一
例子:
在全局配置文件中 configuration內加入:
<!-- 3、typeAliases:別名處理器:可以爲我們的java類型起別名
別名不區分大小寫
-->
<typeAliases>
<!-- 1、typeAlias:爲某個java類型起別名
type:指定要起別名的類型全類名;默認別名就是類名小寫;employee
alias:指定新的別名
-->
<!-- <typeAlias type="bao.Employee" alias="emp"/> -->
<!-- 2、package:爲某個包下的所有類批量起別名
name:指定包名(爲當前包以及下面所有的後代包的每一個類都起一個默認別名(類名小寫),)
-->
<package name="bao"/>
<!-- 3、批量起別名的情況下,使用@Alias註解爲某個類型指定新的別名 -->
</typeAliases>
在User.java進行註解
注意的是,MyBatis已經爲許多常見的 Java 類型內建了相應的類型別名。它們都是大小寫不敏感的,我們在起別名的時候千萬不要佔用已有的別名。
typeHandlers類型處理器
無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
在全局配置文件中
在這裏插入圖片描述
我們可以重寫類型處理器或創建自己的類型處理
器來處理不支持的或非標準的類型。 • 步驟:
• 1)、實現org.apache.ibatis.type.TypeHandler接口或
者繼承org.apache.ibatis.type.BaseTypeHandler
• 2)、指定其映射某個JDBC類型(可選操作)
• 3)、在mybatis全局配置文件中註冊
plugins插件
插件是MyBatis提供的一個非常強大的機制,我們可以通過插件來修改MyBatis的一些核心行爲。插件通過動態代理機制,可以介入四大對象的任何一個方法的執行。
• Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
• ParameterHandler (getParameterObject, setParameters)
• ResultSetHandler (handleResultSets, handleOutputParameters)
• StatementHandler (prepare, parameterize, batch, update, query)
environments環境
• MyBatis可以配置多種環境,比如開發、測試和生產環境需要有不同的配置。
• 每種環境使用一個environment標籤進行配置並指定唯一標識符
• 可以通過environments標籤中的default屬性指定一個環境的標識符來快速的切換環境
在全局配置文件mybatis-config.xml
<!--
4、environments:環境們,mybatis可以配置多種環境 ,default指定使用某種環境。可以達到快速切換環境。
environment:配置一個具體的環境信息;必須有兩個標籤;id代表當前環境的唯一標識
transactionManager:事務管理器;
type:事務管理器的類型;JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
自定義事務管理器:實現TransactionFactory接口.type指定爲全類名
dataSource:數據源;
type:數據源類型;UNPOOLED(UnpooledDataSourceFactory)
|POOLED(PooledDataSourceFactory)
|JNDI(JndiDataSourceFactory)
自定義數據源:實現DataSourceFactory接口,type是全類名
-->
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
databaseIdProvider環境
MyBatis 可以根據不同的數據庫廠商執行不同的語句。
• Type: DB_VENDOR
– 使用MyBatis提供的VendorDatabaseIdProvider解析數據庫廠商標識。也可以實現DatabaseIdProvider接口來自定義。
• Property-name:數據庫廠商標識
• Property-value:爲標識起一個別名,方便SQL語句使用databaseId屬性引用
• DB_VENDOR
– 會通過 DatabaseMetaData#getDatabaseProductName() 返回的字符串進行設置。由於通常情況下這個字符串都非常長而且相同產品的不同版本會返回不同的值,所以最好通過設置屬性別名來使其變短
• MyBatis匹配規則如下:
– 1、如果沒有配置databaseIdProvider標籤,那麼databaseId=null
– 2、如果配置了databaseIdProvider標籤,使用標籤配置的name去匹配數據庫信息,匹配上設置databaseId=配置指定的值,否則依舊爲null
– 3、如果databaseId不爲null,他只會找到配置databaseId的sql語句
– 4、MyBatis 會加載不帶 databaseId 屬性和帶有匹配當前數據庫databaseId 屬性的所有語句。如果同時找到帶有 databaseId 和不帶databaseId 的相同語句,則後者會被捨棄。
在全局配置文件mybatis-config.xml
<!-- 5、databaseIdProvider:支持多數據庫廠商的;
type="DB_VENDOR":VendorDatabaseIdProvider
作用就是得到數據庫廠商的標識(驅動getDatabaseProductName()),mybatis就能根據數據庫廠商標識來執行不同的sql;
MySQL,Oracle,SQL Server,xxxx
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 爲不同的數據庫廠商起別名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
///////////////////////////////////////////////////////////////////////////
///映射文件EmployeeMapper.xml
<select id="getEmpById" resultType="bao.Employee">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="bao.Employee"
databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="bao.Employee"
databaseId="oracle">
select EMPLOYEE_ID id,LAST_NAME lastName,EMAIL email
from employees where EMPLOYEE_ID=#{id}
</select>
mapper映射
在前面的基礎上
mybatis-config.xml
<!-- 6、mappers:將sql映射註冊到全局配置中 -->
<mappers>
<!--
mapper:註冊一個sql映射
註冊配置文件
resource:引用類路徑下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用網路路徑或者磁盤路徑下的sql映射文件
file:///var/mappers/AuthorMapper.xml
註冊接口
class:引用(註冊)接口,
1、有sql映射文件,映射文件名必須和接口同名,並且放在與接口同一目錄下;
2、沒有sql映射文件,所有的sql都是利用註解寫在接口上;
推薦:
比較重要的,複雜的Dao接口我們來寫sql映射文件
不重要,簡單的Dao接口爲了開發快速可以使用註解;
-->
<!-- <mapper resource="bao/EmployeeMapper.xml"/> -->
<!-- <mapper class="bao.EmployeeMapperAnnotation"/> -->
<!-- 批量註冊:這種方式要求SQL映射文件名必須和接口名相同並且在同一目錄下-->
<package name="bao"/>
</mappers>
////////////////////////////////////////////////////////////////////////////
//接口1
package bao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
//////////////////////////////////////////////////////////////////////////
//接口2
package bao;
import org.apache.ibatis.annotations.Select;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id=#{id}")
public Employee getEmpById(Integer id);
}
MyBatis-映射文件
• 映射文件指導着MyBatis如何進行數據庫增刪改查,有着非常重要的意義;
•cache –命名空間的二級緩存配置
•cache-ref – 其他命名空間緩存配置的引用。
•resultMap – 自定義結果集映射
•parameterMap – 已廢棄!老式風格的參數映射
•sql –抽取可重用語句塊。
•insert – 映射插入語句
•update – 映射更新語句
•delete – 映射刪除語句
•select – 映射查詢語句
insert、update、delete元素
例子:
EmployeeMapper接口改爲
package bao;
import org.apache.ibatis.annotations.Select;
public interface EmployeeMapper {
public User getEmpById(String id);
public Long addEmp(User employee);
public boolean updateEmp(User employee);
public void deleteEmpById(String s);
}
/////////////////////////////////////////////////////////////////////////////
//映射文件
<?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="bao.EmployeeMapper">
<select id="getEmpById" resultType="emp">
select * from USER1 where USERNAME = #{username}
</select>
<insert id="addEmp" ><!--parameterType:參數類型,可以省略,-->
insert into USER1(USERNAME,PASSWORD) values(#{username},#{password})
</insert>
<update id="updateEmp">
update USER1 set PASSWORD=#{password}
where USERNAME=#{username}
</update>
<delete id="deleteEmpById">
delete from
USER1
where USERNAME=#{username}
</delete>
</mapper>
///////////////////////////////////////////////////////////////////////
//測試類
package bao;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* 測試增刪改
* 1、mybatis允許增刪改直接定義以下類型返回值
* Integer、Long、Boolean、void
* 2、我們需要手動提交數據
* sqlSessionFactory.openSession();===》手動提交
* sqlSessionFactory.openSession(true);===》自動提交
*/
public class MyBatisTest {
public static void main(String []args) throws IOException {
// 1、獲取sqlSessionFactory對象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、獲取sqlSession對象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、獲取接口的實現類對象
//會爲接口自動的創建一個代理對象,代理對象去執行增刪改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
User employee = new User("user","woain");
mapper.addEmp(employee);
System.out.println(mapper.updateEmp(employee));
mapper.deleteEmpById("user");
openSession.commit();
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
}
主鍵生成方式
1.
若數據庫支持自動生成主鍵的字段(比如 MySQL 和 SQL Server),則可以設置
useGeneratedKeys=”true”,然後再把keyProperty 設置到目標屬性上。
將上面映射文件中的添加語句改爲
/**獲取自增主鍵的值:
mysql支持自增主鍵,自增主鍵值的獲取,mybatis也是利用statement.getGenreatedKeys();
useGeneratedKeys="true";使用自增主鍵獲取主鍵值策略
keyProperty;指定對應的主鍵屬性,也就是mybatis獲取到主鍵值以後,將這個值封裝給javaBean的哪個屬性*/
<insert id="addEmp" useGeneratedKeys="true" keyProperty="username">
insert into USER1(PASSWORD) values(#{password})
</insert>
上面測試類部分改爲(前提在數據庫表中username設爲自增字段)
User employee = new User(null,"26woain");
mapper.addEmp(employee);
System.out.println(employee.getUsername());
//mapper.deleteEmpById("user");
openSession.commit();
2.
而對於不支持自增型主鍵的數據庫(例如Oracle),則可以使用 selectKey 子元素:
selectKey 元素將會首先運行,id 會被設置,然後插入語句會被調用
在映射文件中加入
<!--
獲取非自增主鍵的值:
Oracle不支持自增;Oracle使用序列來模擬自增;
每次插入的數據的主鍵是從序列中拿到的值;如何獲取到這個值;
-->
<insert id="addEmp" databaseId="oracle">
<!--
keyProperty:查出的主鍵值封裝給javaBean的哪個屬性
order="BEFORE":當前sql在插入sql之前運行
AFTER:當前sql在插入sql之後運行
resultType:查出的數據的返回值類型
BEFORE運行順序:
先運行selectKey查詢id的sql;查出id值封裝給javaBean的id屬性
在運行插入的sql;就可以取出id屬性對應的值
AFTER運行順序:
先運行插入的sql(從序列中取出新值作爲id);
再運行selectKey查詢id的sql;
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 編寫查詢主鍵的sql語句 -->
<!-- BEFORE-->
select EMPLOYEES_SEQ.nextval from dual
<!-- AFTER:
select EMPLOYEES_SEQ.currval from dual -->
</selectKey>
<!-- 插入時的主鍵是從序列中拿到的 -->
<!-- BEFORE:-->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->})
<!-- AFTER:
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(employees_seq.nextval,#{lastName},#{email}) -->
</insert>
參數(Parameters)傳遞
單個參數:mybatis不會做特殊處理,
#{參數名/任意名}:取出參數值。
多個參數:mybatis會做特殊處理。
多個參數會被封裝成 一個map,
key:param1…paramN,或者參數的索引也可以
value:傳入的參數值
#{}就是從map中獲取指定的key的值;
異常:
org.apache.ibatis.binding.BindingException:
Parameter ‘id’ not found.
Available parameters are [1, 0, param1, param2]
操作:
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
取值:#{id},#{lastName}
例子
全局配置文件EmployeeMapper.xml改善
//接口查詢多參可看下面
<select id="getEmpById" resultType="emp">
select * from USER1 where USERNAME = #{param1} and Password=#{param2}
</select>
【命名參數】:明確指定封裝參數時map的key;@Param(“id”)
多個參數會被封裝成 一個map,
key:使用@Param註解指定的值
value:參數值
#{指定的key}取出對應的參數值
接口變化如下
//bao.EmployeeMapper
public User getEmpById(@Param("id")String id,@Param("s")String s);
//映射文件查詢變化
select * from USER1 where USERNAME = #{id} and Password=#{s}
POJO:
如果多個參數正好是我們業務邏輯的數據模型,我們就可以直接傳入pojo;
#{屬性名}:取出傳入的pojo的屬性值
Map:
如果多個參數不是業務模型中的數據,沒有對應的pojo,不經常使用,爲了方便,我們也可以傳入map
#{key}:取出map中對應的值
例子:
//EmployeeMapper.java在上面基礎上加入
public User getmap(Map<String, Object> map);
//////////////////////////////////////////////////////////////////////////
//映射文件在上面基礎上EmployeeMapper.xml
<select id="getmap" resultType="emp">
select * from USER1 where USERNAME = #{id}
</select>
///////////////////////////////////////////////////////////////////////
//測試文件MybatisText.java
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
User employee = mapper.getmap(map);
System.out.println(employee);
TO:
如果多個參數不是業務模型中的數據,但是經常要使用,推薦來編寫一個TO(Transfer Object)數據傳輸對象
Page{
int index;
int size;
}
拓展:
public Employee getEmp(@Param(“id”)Integer id,String lastName);
取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(Integer id,@Param(“e”)Employee emp);
取值:id==>#{param1} lastName===>#{param2.lastName/e.lastName}
注意:如果是Collection(List、Set)類型或者是數組,也會特殊處理。也是把傳入的list或者數組封裝在map中。
key:Collection(collection),如果是List還可以使用這個key(list)
數組(array)
public Employee getEmpById(List ids);
取值:取出第一個id的值: #{list[0]}
參數值的獲取
#{}:可以獲取map中的值或者pojo對象屬性的值;
${}:可以獲取map中的值或者pojo對象屬性的值;
select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
區別:
#{}:是以預編譯的形式,將參數設置到sql語句中;PreparedStatement;防止sql注入
${}:取出的值直接拼裝在sql語句中;會有安全問題;
大多情況下,我們去參數的值都應該去使用#{};
原生jdbc不支持佔位符的地方我們就可以使用${}進行取值
比如分表、排序。。。;按照年份分表拆分
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
例子:
在以前的基礎上
//測試bao.MyBatisTest
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
map.put("user", "USER1");
User employee = mapper.getmap(map);
System.out.println(employee);
openSession.commit()
///////////////////////////////////////////////////////////////////////////
//映射文件/mybaits/src/bao/EmployeeMapper.xml
<select id="getmap" resultType="emp">
select * from ${user} where USERNAME = ${id}
</select>
#{}:更豐富的用法:
規定參數的一些規則:
javaType、 jdbcType、 mode(存儲過程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未來準備支持的功能);
jdbcType通常需要在某種特定的條件下被設置:
在我們數據爲null的時候,有些數據庫可能不能識別mybatis對null的默認處理。比如Oracle(報錯);
JdbcType OTHER:無效的類型;因爲mybatis對所有的null都映射的是原生Jdbc的OTHER類型,oracle不能正確處理;
由於全局配置中:jdbcTypeForNull=OTHER;oracle不支持;兩種辦法
1、#{email,jdbcType=NULL};
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>
select元素
• Select元素來定義查詢操作。
• Id:唯一標識符。
– 用來引用這條語句,需要和接口的方法名一致
• parameterType:參數類型。 – 可以不傳,MyBatis會根據TypeHandler自動推斷
• resultType:返回值類型。 – 別名或者全類名,如果返回的是集合,定義集合中元
素的類型。不能和resultMap同時使用
例子:
以前面的例子做基礎:
//接口bao.EmployeeMapper.java
public List<User> getEmpsByLastNameLike(String lastName);
//映射文件/mybaits/wenjian/EmployeeMapper.xml
<select id="getEmpsByLastNameLike" resultType="bao.User">
select * from USER1 where password like #{lastName}
</select>
//測試類
System.out.println(mapper.getEmpsByLastNameLike("1%"));
映射文件select記錄封裝map
以前面的例子爲基礎
例子:
//接口bao.EmployeeMapper.java
//多條記錄封裝一個map:Map<Integer,Employee>:鍵是這條記錄的主鍵,值是記錄封裝後的javaBean
//@MapKey:告訴mybatis封裝這個map的時候使用哪個屬性作爲map的key
@MapKey("username")
public Map<String, User> getEmpByLastNameLikeReturnMap(String lastName);
//返回一條記錄的map;key就是列名,值就是對應的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
///////////////////////////////////////////////////////////////////////////
//映射文件/mybaits/wenjian/EmployeeMapper.xml
<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="bao.User">
select * from USER1 where password like #{lastName}
</select>
<!--public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from USER1 where username=#{id}
</select>
////////////////////////////////////////////////////////////////////////////測試
System.out.println(mapper.getEmpByIdReturnMap(1)); System.out.println(mapper.getEmpByLastNameLikeReturnMap("123"));
自動映射
1、全局setting設置
– autoMappingBehavior默認是PARTIAL,開啓自動映射的功能。唯一的要求是列名和javaBean屬性名一致
– 如果autoMappingBehavior設置爲null則會取消自動映射
– 數據庫字段命名規範,POJO屬性符合駝峯命名法,如A_COLUMNaColumn,我們可以開啓自動駝峯命名規則映射功能,mapUnderscoreToCamelCase=true。
2、自定義resultMap,實現高級結果集映射。
• constructor - 類在實例化時, 用來注入結果到構造方法中
– idArg - ID 參數; 標記結果作爲 ID 可以幫助提高整體效能
– arg - 注入到構造方法的一個普通結果
• id – 一個 ID 結果; 標記結果作爲 ID 可以幫助提高整體效能
• result – 注入到字段或 JavaBean 屬性的普通結果
• association – 一個複雜的類型關聯;許多結果將包成這種類型
– 嵌入結果映射 – 結果映射自身的關聯,或者參考一個
• collection – 複雜類型的集 – 嵌入結果映射 – 結果映射自身的集,或者參考一個
• discriminator – 使用結果值來決定使用哪個結果映射
– case – 基於某些值的結果映射
• 嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相同的元
素,或者它可以參照一個外部的結果映射。
_select_resultMap_關聯查詢_環境搭建
例子(以前面的例子爲基礎):
//接口bao.EmployeeMapper.java
public User getEmpById1(Integer id);
//////////////////////////////////////////////////////////////////////
//映射文件/mybaits/wenjian/EmployeeMapper.xml
<!--自定義某個javaBean的封裝規則
type:自定義規則的Java類型
id:唯一id方便引用
-->
<resultMap type="bao.User" id="MySimpleEmp">
<!--指定主鍵列的封裝規則
id定義主鍵會底層有優化;
column:指定哪一列
property:指定對應的javaBean屬性
-->
<id column="username" property="username"/>
<!-- 定義普通列封裝規則 -->
<result column="password" property="password"/>
<!-- 其他不指定的列會自動封裝:我們只要寫resultMap就把全部的映射規則都寫上。 -->
</resultMap>
<!-- resultMap:自定義結果集映射規則; -->
<!-- public Employee getEmpById1(Integer id); -->
<select id="getEmpById1" resultMap="MySimpleEmp">
select * from USER1 where username=#{id}
</select>
///////////////////////////////////////////////
//測試類
System.out.println(mapper.getEmpById1(1));
_select_resultMap_關聯查詢_級聯屬性封裝結果
例子(在以前的例子基礎上):
//接口bao.EmployeeMapper.java
public User getEmpAndDept(Integer id);
/////////////////////////////////////////////////////////////////////
//User.java 改變如下:
package bao;
import org.apache.ibatis.type.Alias;
public class User {
private int username;
private String password;
private Department dpartment;
public Department getDpartment() {
return dpartment;
}
public void setDpartment(Department dpartment) {
this.dpartment = dpartment;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUsername() {
return username;
}
public void setUsername(int username) {
this.username = username;
}
@Override
public String toString() {
return "User [dpartment=" + dpartment + ", password=" + password
+ ", username=" + username + "]";
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(int username, String password, Department dpartment) {
super();
this.username = username;
this.password = password;
this.dpartment = dpartment;
}
}
///mybaits/src/bao/Department.java
package bao;
public class Department {
private int id;
private String name;
public Department() {
super();
// TODO Auto-generated constructor stub
}
public Department(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
///////////////////////////////////////////////////////////////////////////
////映射文件/mybaits/wenjian/EmployeeMapper.xml
<!--
場景一:
查詢User的同時查詢員工對應的部門
User===Department
一個員工有與之對應的部門信息;
-->
<!--
聯合查詢:級聯屬性封裝結果集
-->
<resultMap type="bao.User" id="MyDifEmp2">
<id column="username" property="username"/>
<result column="password" property="password"/>
<result column="did" property="dpartment.id"/>
<result column="name" property="dpartment.name"/>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp2">
SELECT * FROM USER1,DEPARTMENT
WHERE USER1.DID=DEPARTMENT.ID AND USER1.USERNAME=#{id}
</select>
/////////////////////////////////////////////////////////////////////
//測試
System.out.println(mapper.getEmpAndDept(1));
id & result
• id 和 result 映射一個單獨列的值到簡單數據類型(字符串,整型,雙精度浮點數,日期等)的屬性或字段。
association
複雜對象映射
• POJO中的屬性可能會是一個對象
• 我們可以使用聯合查詢,並以級聯屬性的方式封裝對象。
• 使用association標籤定義對象的封裝規則
association-嵌套結果集
例子(以前面的例子爲基礎):
////映射文件/mybaits/wenjian/EmployeeMapper.xml
<!--
使用association定義關聯的單個對象的封裝規則;
-->
<resultMap type="bao.User" id="MyDifEmp">
<id column="username" property="username"/>
<result column="password" property="password"/>
<!-- association可以指定聯合的javaBean對象-->
<!-- property="dept":指定哪個屬性是聯合的對象-->
<!-- javaType:指定這個屬性對象的類型[不能省略]-->
<association property="dpartment" javaType="bao.Department">
<id column="did" property="id"/>
<result column="name" property="name"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT * FROM USER1,DEPARTMENT
WHERE USER1.DID=DEPARTMENT.ID AND USER1.USERNAME=#{id}
</select>
_select_resultMap_關聯查詢_association分步查詢
例子(以前面的例子爲基礎):
//1.給Department.java配置相應的接口DepartmentMapper.java
package bao;
public interface DepartmentMapper {
public Department getEmpById10(Integer id);
}
//2..給Department.java配置相應的映射文件DepartmentMapper.xml
<?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="bao.DepartmentMapper">
<select id="getEmpById10" resultType="bao.Department">
select * from DEPARTMENT where id=#{id}</select>
</mapper>
//3.映射文件DepartmentMapper.xml註冊在全局配置文件中
<mappers>
<mapper resource="EmployeeMapper.xml" />
<mapper resource="DepartmentMapper.xml" />
</mappers>
//4.bao.EmployeeMapper.java接口加入
public User getEmpByIdStep(Integer id);
///////////////////////////////////////////////////////////////////
//5.映射文件EmployeeMapper.xml加入
<!-- 使用association進行分步查詢:
1、先按照員工id查詢員工信息
2、根據查詢員工信息中的did值去部門表查出部門信息
3、部門設置到員工中;
-->
<resultMap type="bao.User" id="MyEmpByStep">
<id column="username" property="username"/>
<result column="password" property="password"/>
<!-- association定義關聯對象的封裝規則
select:表明當前屬性是調用select指定的方法查出的結果
column:指定將哪一列的值傳給這個方法
流程:使用select指定的方法(傳入column指定的這列參數的值)查出對象,並封裝給property指定的屬性
-->
<association property="dpartment"
select="bao.DepartmentMapper.getEmpById10"
column="did">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from USER1 where username=#{id}
</select>
//6.測試
System.out.println(mapper.getEmpByIdStep(1));
_select_resultMap_關聯查詢_分步查詢&延遲加載
延遲加載
MyBatis中的延遲加載,也稱爲懶加載,是指在進行關聯查詢的時候,按照設 置延遲加載規則推遲對關聯對象的select檢索。延遲加載可以有效的減少數據庫 的壓力。
注意:MyBatis的延遲加載只是對關聯對象的查詢有延遲設置,對於主加載對象 都是直接執行查詢語句的。
詳細解釋
例子(以前面的例子爲基礎):
在全局配置文件/mybaits/wenjian/mybatis-config.xml加入(properties之後typeAliases之前)
<settings>
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
<setting name="jdbcTypeForNull" value="NULL"/>
<!--顯示的指定每個我們需要更改的配置的值,即使他是默認的。防止版本更新帶來的問題 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
_select_resultMap_關聯查詢_collection定義關聯集合封裝規
例子(以前面的例子爲基礎):
//bao.Department.java 加入以下屬性以及getset
private List<User> list;
//bao.DepartmentMapper.java
public Department getDeptByIdPlus(Integer id);
///wenjian/DepartmentMapper.xml
<!--嵌套結果集的方式,使用collection標籤定義關聯的集合類型的屬性封裝規則 -->
<resultMap type="bao.Department" id="MyDept">
<id column="did" property="id"/>
<result column="name" property="name"/>
<!--
collection定義關聯集合類型的屬性的封裝規則
ofType:指定集合裏面元素的類型
-->
<collection property="list" ofType="bao.User">
<!-- 定義這個集合中元素的封裝規則 -->
<id column="username" property="username"/>
<result column="password" property="password"/>
</collection>
</resultMap>
<select id="getDeptByIdPlus" resultMap="MyDept">
select * from USER1,DEPARTMENT where USER1.DID=DEPARTMENT.ID and id=#{id}</select>
//測試
DepartmentMapper mapper = openSession.getMapper( DepartmentMapper.class);
System.out.println(mapper.getDeptByIdPlus(1));
_select_resultMap_關聯查詢_collection分步查詢&延遲加載
例子(以前面的例子爲基礎):
//bao.DepartmentMapper.java
public Department getDeptByIdStep(Integer id);
///wenjian/DepartmentMapper.xml
<!-- collection:分段查詢 -->
<resultMap type="bao.Department" id="MyDeptStep">
<id column="id" property="id"/>
<id column="name" property="name"/>
<collection property="list"
select="bao.EmployeeMapper.getEmpsByDeptId"
column="id"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select * from DEPARTMENT where id=#{id}
</select>
///mybaits/src/bao/EmployeeMapper.java
public List<User> getEmpsByDeptId(Integer id);
///mybaits/wenjian/EmployeeMapper.xml
<!--
場景二:
查詢部門的時候將部門對應的所有員工信息也查詢出來:註釋在DepartmentMapper.xml中
-->
<!-- public List<Employee> getEmpsByDeptId(Integer id); -->
<select id="getEmpsByDeptId" resultType="bao.User">
select * from USER1 where did=#{id}
</select>
//測試
System.out.println(mapper.getDeptByIdStep(1));
_select_resultMap_分步查詢傳遞多列值&fetchType
例子(以前面的例子爲基礎):
///wenjian/DepartmentMapper.xml
<!-- collection:分段查詢 -->
<resultMap type="bao.Department" id="MyDeptStep">
<id column="id" property="id"/>
<id column="dept_name" property="departmentName"/>
<collection property="emps"
select="bao.EmployeeMapper.getEmpsByDeptId"
column="{id=id}" fetchType="lazy"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select * from DEPARTMENT where id=#{id}
</select>
<!-- 擴展:多列的值傳遞過去:
將多列的值封裝map傳遞;
column="{key1=column1,key2=column2}"//key1值是傳進參數變量,後面是值
fetchType="lazy":表示使用延遲加載;
- lazy:延遲
- eager:立即
-->
_select_resultMap_discriminator鑑別器
例子(以前面的例子爲基礎):
///mybaits/wenjian/EmployeeMapper.xml
<resultMap type="bao.User" id="MyEmpDis">
<id column="username" property="username"/>
<result column="password" property="password"/>
<!--
column:指定判定的列名
javaType:列值對應的java類型 -->
<discriminator javaType="string" column="password">
<!-- resultType:指定封裝的結果類型;不能缺少。/resultMap
password=="26"時如下賦值-->
<case value="26" resultType="bao.User">
<association property="dpartment"
select="bao.DepartmentMapper.getEmpById10"
column="did">
</association>
</case>
<!--password=="123"時如下賦值 -->
<case value="123" resultType="bao.User">
<id column="username" property="username"/>
<result column="username" property="password"/>
</case>
</discriminator>
</resultMap>