1 MyBatis簡介
- MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名爲MyBatis,實質上Mybatis對ibatis進行一些改進。
- MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如註冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
- 對jdbc的封裝框架有哪些:Hibernate,dbutils,jdbcTemplate[spring],mybatis
- 原理:Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。
1.1 MyBatis的框架核心
- mybatis配置文件,包括Mybatis全局配置文件和Mybatis映射文件,其中全局配置文件配置了數據源、事務等信息;映射文件配置了SQL執行相關的 信息。
- mybatis通過讀取配置文件信息(全局配置文件和映射文件),構造出
SqlSessionFactory
,即會話工廠。 - 通過SqlSessionFactory,可以創建
SqlSession
即會話。Mybatis是通過SqlSession來操作數據庫的。 - SqlSession本身不能直接操作數據庫,它是通過底層的
Executor
執行器接口來操作數據庫的。Executor接口有兩個實現類,一個是普通執行器,一個是緩存執行器(默認)。 - Executor執行器要處理的SQL信息是封裝到一個底層對象MappedStatement中。該對象包括:SQL語句、輸入參數映射信息、輸出結果集映射信息。其中輸入參數和輸出結果的映射類型包括HashMap集合對象、POJO對象類型。
2 Mybatis 案例
2.1 環境搭建
- 新建數據庫,添加表
/*
SQLyog v10.2
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `items` */
CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '商品名稱',
`price` float(10,1) NOT NULL COMMENT '商品定價',
`detail` text COMMENT '商品描述',
`pic` varchar(64) DEFAULT NULL COMMENT '商品圖片',
`createtime` datetime NOT NULL COMMENT '生產日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Table structure for table `orderdetail` */
CREATE TABLE `orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orders_id` int(11) NOT NULL COMMENT '訂單id',
`items_id` int(11) NOT NULL COMMENT '商品id',
`items_num` int(11) DEFAULT NULL COMMENT '商品購買數量',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`orders_id`),
KEY `FK_orderdetail_2` (`items_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*Table structure for table `orders` */
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下單用戶id',
`number` varchar(32) NOT NULL COMMENT '訂單號',
`createtime` datetime NOT NULL COMMENT '創建訂單時間',
`note` varchar(100) DEFAULT NULL COMMENT '備註',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*Table structure for table `user` */
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用戶名稱',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性別',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
添加數據
/*
SQLyog v10.2
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Data for the table `items` */
insert into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'臺式機',3000.0,'該電腦質量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'筆記本',6000.0,'筆記本性能好,質量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'揹包',200.0,'名牌揹包,容量大質量好!!!!',NULL,'2015-02-06 13:23:02');
/*Data for the table `orderdetail` */
insert into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);
/*Data for the table `orders` */
insert into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);
/*Data for the table `user` */
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'張三','2014-07-10','1','北京市'),(16,'張小明',NULL,'1','河南鄭州'),(22,'陳小明',NULL,'1','河南鄭州'),(24,'張三丰',NULL,'1','河南鄭州'),(25,'陳小明',NULL,'1','河南鄭州'),(26,'王五',NULL,NULL,NULL);
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
2.2 導入jar
2.3 添加log4j.properties
- Mybatis使用的日誌包是log4j的,所以需要添加log4j.properties。
- 在classpath下創建log4j.properties如下:【文件內容可以從mybatis-3.2.7.pdf中拷貝】
- 日誌級別在開發階段設置成DEBUG,在生產階段設置成INFO或者ERROR
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
2.4 開發步驟
- 創建PO(model)類,根據需求創建;
- 創建全局配置文件SqlMapConfig.xml;
- 編寫映射文件;
- 加載映射文件,在SqlMapConfig.xml中進行加載;
- 編寫測試程序,即編寫Java代碼,連接並操作數據庫。
思路:
6. 讀取配置文件;
7. 通過SqlSessionFactoryBuilder創建SqlSessionFactory會話工廠。
8. 通過SqlSessionFactory創建SqlSession。
9. 調用SqlSession的操作數據庫方法。
10. 關閉SqlSession。
2.4.1 創建 PO 類
package com.tzb.model;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private int id;
private String username;// 用戶姓名
private String sex;// 性別
private Date birthday;// 生日
private String address;// 地址
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
2.4.2 創建 SqlMapConfig.xml
在classpath(src)下,創建SqlMapConfig.xml文件【SqlMapConfig.xml(文件頭可以從mybatis-3.2.7.pdf文檔的2.1.2小節中拷貝)】
<?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>
<!-- 配置mybatis的環境信息 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事務控制,由mybatis進行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置數據源,採用dbcp連接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
2.4.3 映射文件
在classpath下,創建sqlmap文件夾。在sqlmap目錄下,創建User.xml映射文件。
【Mybatis的映射文件頭(可以從mybatis-3.2.7.pdf文件中拷貝)】
<?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:命名空間,它的作用就是對SQL進行分類化管理,可以理解爲SQL隔離
注意:使用mapper代理開發時,namespace有特殊且重要的作用
-->
<mapper namespace="test">
<!--
[id]:statement的id,要求在命名空間內唯一
[parameterType]:入參的java類型
[resultType]:查詢出的單條結果集對應的java類型
[#{}]: 表示一個佔位符?
[#{id}]:表示該佔位符待接收參數的名稱爲id。注意:如果參數爲簡單類型時,#{}裏面的參數名稱可以是任意定義
-->
<select id="findUserById" parameterType="int" resultType="com.tzb.model.User">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
2.4.4 配置文件加載映射文件
- 測試類
import com.tzb.model.User;
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;
import java.io.IOException;
import java.io.InputStream;
public class Demo1 {
@Test
public void test1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
User user = session.selectOne("findUserById", 10);
System.out.println(user);
session.close();
}
}
3 案例
3.1 模糊查詢
- User.xml
<!--
[${}]:表示拼接SQL字符串
[${value}]:表示要拼接的是簡單類型參數。
注意:
1、如果參數爲簡單類型時,${}裏面的參數名稱必須爲value
2、${}會引起SQL注入,一般情況下不推薦使用。但是有些場景必須使用${},比如order by ${colname}
-->
<select id="findUserByName" parameterType="String" resultType="com.tzb.model.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
- 測試類
@Test
public void test1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
/*User user = session.selectOne("findUserById", 10);
System.out.println(user);*/
List<User> users = session.selectList("findUserByName", "張");
System.out.println(users);
session.close();
}
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 964635168.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@397f2a20]
DEBUG [main] - ==> Preparing: SELECT * FROM USER WHERE username LIKE '%張%'
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 3
[User [id=10, username=張三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市], User [id=16, username=張小明, sex=1, birthday=null, address=河南鄭州], User [id=24, username=張三丰, sex=1, birthday=null, address=河南鄭州]]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@397f2a20]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@397f2a20]
DEBUG [main] - Returned connection 964635168 to pool.
Process finished with exit code 0
3.2 用戶插入
- User.xml
<!--
這裏的佔位寫模型的屬性
-->
<insert id="insertUser" parameterType="com.tzb.model.User">
INSERT INTO user (username,sex,birthday,address) VALUE (#{username},#{sex},#{birthday},#{address});
</insert>
- 測試類
import com.tzb.model.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class Demo1 {
private SqlSession session;
@Before
public void before() throws IOException {
System.out.println("before.......獲取 session");
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
session = factory.openSession();
}
@After
public void after() {
System.out.println("after.......關閉 session");
session.close();
}
@Test
public void test1() throws IOException {
User user = new User("Jenny", "1", new Date(), "上海");
int row = session.insert("insertUser", user);
session.commit();
System.out.println("受影響的行數:" + row);
}
}
3.3 刪除
- User.xml
<!--刪除-->
<delete id="deleteUser" parameterType="int">
delete From user where id=#{id};
</delete>
@Test
public void test2() throws IOException {
int row = session.delete("deleteUser", 30);
session.commit();
System.out.println("受影響的行數:" + row);
}
3.4 更新
- User.xml
<!--更新-->
<delete id="updateUser" parameterType="com.tzb.model.User">
UPDATE user SET sex=#{sex},address=#{address}
WHERE id=#{id};
</delete>
/*更新*/
@Test
public void test3() throws IOException {
User user = new User();
user.setId(31);
user.setSex("男");
user.setAddress("天津");
int row = session.update("updateUser", user);
session.commit();
System.out.println("受影響的行數:" + row);
}
3.5 插入後自動返回主鍵
- MySQL自增主鍵,是指在insert之前MySQL會自動生成一個自增的主鍵。
- 我們可以通過MySQL的函數獲取到剛插入的自增主鍵:
LAST_INSERT_ID() - 這個函數是在insert語句之後去調用。
- User.xml
<!--插入時自動返回主鍵id-->
<insert id="insertUser2" parameterType="com.tzb.model.User">
<!--
[selectKey標籤]:通過select查詢來生成主鍵
[keyProperty]:指定存放生成主鍵的屬性
[resultType]:生成主鍵所對應的Java類型
[order]:指定該查詢主鍵SQL語句的執行順序,相對於insert語句
[last_insert_id]:MySQL的函數,要配合insert語句一起使用 -->
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user (username,sex,birthday,address)
VALUE (#{username},#{sex},#{birthday},#{address});
</insert>
@Test
public void test4() throws IOException {
User user = new User("Jenny", "1", new Date(), "上海");
int row = session.insert("insertUser2", user);
session.commit();
System.out.println("受影響的行數:" + row);
System.out.println("用戶的ID:" + user.getId());
}
4 總結
4.1 parameterType
和resultType
parameterType指定輸入參數的java類型,可以填寫別名或Java類的全限定名。
resultType指定輸出結果的java類型,可以填寫別名或Java類的全限定名。
4.2 #{}
和${}
#{}
:相當於預處理中的佔位符?。#{}
裏面的參數表示接收java輸入參數的名稱。#{}
可以接受HashMap、POJO類型的參數。
當接受簡單類型的參數時,#{}裏面可以是value,也可以是其他。#{}
可以防止SQL注入。
-${}
:相當於拼接SQL串,對傳入的值不做任何解釋的原樣輸出。${}
會引起SQL注入,所以要謹慎使用。${}
可以接受HashMap、POJO類型的參數。
當接受簡單類型的參數時,${}
裏面只能是value。
4.3 selectOne
和selectList
selectOne:只能查詢0或1條記錄,大於1條記錄的話,會報錯:
selectList:可以查詢0或N條記錄