Appearance
https://mybatis.org/mybatis-3/zh/getting-started.html
概述
框架
软件框架,是为某些特定领域解决特定问题,所提供的通用的、完整的解决方案。
ORM
Object Relational Mapping
框架对比
JDBC
Hibernate
全自动ORM框架,不需要编写SQL语句,由Hibernate内核识别语义后,自动翻译SQL语句 编码效率高,执行效率低
MyBatis
半自动ORM框架,需要编写SQL语句 编码效率低,执行效率高
入门
快速入门
导入数据库脚本
sql
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50728
Source Host : localhost
Source Database : db_shopping
Target Server Type : MySQL
Target Server Version : 50728
File Encoding : utf-8
Date: 02/09/2021 21:16:46 PM
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `tb_address`
-- ----------------------------
DROP TABLE IF EXISTS `tb_address`;
CREATE TABLE `tb_address` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`detail` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`mobile` varchar(255) NOT NULL,
`def` tinyint(4) DEFAULT '0',
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `tb_address_ibfk_uid` FOREIGN KEY (`uid`) REFERENCES `tb_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `tb_cart`
-- ----------------------------
DROP TABLE IF EXISTS `tb_cart`;
CREATE TABLE `tb_cart` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` double NOT NULL,
`count` int(11) NOT NULL,
`uid` int(11) DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
KEY `pid` (`pid`),
CONSTRAINT `tb_cart_ibfk_pid` FOREIGN KEY (`pid`) REFERENCES `tb_product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `tb_cart_ibfk_uid` FOREIGN KEY (`uid`) REFERENCES `tb_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_cart`
-- ----------------------------
BEGIN;
INSERT INTO `tb_cart` VALUES ('1', '9000', '1', '1', '1'), ('2', '12000', '1', '1', '2');
COMMIT;
-- ----------------------------
-- Table structure for `tb_category`
-- ----------------------------
DROP TABLE IF EXISTS `tb_category`;
CREATE TABLE `tb_category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`parent` int(11) DEFAULT NULL,
`level` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `parent` (`parent`),
CONSTRAINT `tb_category_ibfk_parent` FOREIGN KEY (`parent`) REFERENCES `tb_category` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=110 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_category`
-- ----------------------------
BEGIN;
INSERT INTO `tb_category` VALUES ('1', '家用电器', null, '1'), ('2', '电视', '1', '2'), ('3', '空调', '1', '2'), ('4', '洗衣机', '1', '2'), ('5', '冰箱', '1', '2'), ('6', '厨卫大电', '1', '2'), ('7', '厨房小电', '1', '2'), ('8', '生活电器', '1', '2'), ('9', '个护健康', '1', '2'), ('10', '视听影音', '1', '2'), ('11', '超薄电视', '2', '3'), ('12', '全面屏电视', '2', '3'), ('13', '智能电视', '2', '3'), ('14', '教育电视', '2', '3'), ('15', 'OLED电视', '2', '3'), ('16', '智慧屏', '2', '3'), ('17', '4K超清电视', '2', '3'), ('18', '55英寸', '2', '3'), ('19', '空调挂机', '3', '3'), ('20', '空调柜机', '3', '3'), ('21', '中央空调', '3', '3'), ('22', '变频空调', '3', '2'), ('23', '一级能效', '3', '2'), ('24', '移动空调', '3', '2'), ('25', '以旧换新', '3', '2'), ('26', '滚筒洗衣机', '4', '3'), ('27', '洗烘一体机', '4', '3'), ('28', '波轮洗衣机', '4', '3'), ('29', '迷你洗衣机', '4', '3'), ('30', '烘干机', '4', '3'), ('31', '洗衣机配件', '4', '3'), ('32', '多门', '5', '3'), ('33', '对开门', '5', '3'), ('34', '三门', '5', '3'), ('35', '双门', '5', '3'), ('36', '冷柜/冰吧', '5', '3'), ('37', '酒柜', '5', '3'), ('38', '冰箱配件', '5', '3'), ('39', '油烟机', '6', '3'), ('40', '燃气灶', '6', '3'), ('41', '烟灶套装', '6', '3'), ('42', '集成灶', '6', '3'), ('43', '消毒柜', '6', '3'), ('44', '洗碗机', '6', '3'), ('45', '电热水器', '6', '3'), ('46', '燃气热水器', '6', '3'), ('47', '空气能热水器', '6', '3'), ('48', '太阳能热水器', '6', '3'), ('49', '嵌入式厨电', '6', '3'), ('50', '烟机灶具配件', '6', '3'), ('51', '破壁机', '7', '3'), ('52', '电烤箱', '7', '3'), ('53', '电饭煲', '7', '3'), ('54', '电压力锅', '7', '3'), ('55', '电炖锅', '7', '3'), ('56', '豆浆机', '7', '3'), ('57', '料理机', '7', '3'), ('58', '咖啡机', '7', '3'), ('59', '电饼铛', '7', '3'), ('60', '榨汁机/原汁机', '7', '3'), ('61', '电水壶/热水瓶', '7', '3'), ('62', '微波炉', '7', '3'), ('63', '电热饭盒', '7', '3'), ('64', '电火锅', '7', '3'), ('65', '养生壶', '7', '3'), ('66', '电磁炉', '7', '3'), ('67', '面包机', '7', '3'), ('68', '空气炸锅', '7', '3'), ('69', '面条机', '7', '3'), ('70', '电陶炉', '7', '3'), ('71', '煮蛋器', '7', '3'), ('72', '电燃烧炉', '7', '3'), ('73', '电风扇', '8', '3'), ('74', '冷风扇', '8', '3'), ('75', '空气净化器', '8', '3'), ('76', '吸尘器', '8', '3'), ('77', '除螨仪', '8', '3'), ('78', '扫地机器人', '8', '3'), ('79', '除湿机', '8', '3'), ('80', '干衣机', '8', '3'), ('81', '蒸汽拖把/拖地机', '8', '3'), ('82', '挂烫机/熨斗', '8', '3'), ('83', '电话机', '8', '3'), ('84', '饮水机', '8', '3'), ('85', '净水器', '8', '3'), ('86', '取暖器', '8', '3'), ('87', '加湿器', '8', '3'), ('88', '毛球修剪器', '8', '3'), ('89', '生活电器配件', '8', '3'), ('90', '剃须刀', '9', '3'), ('91', '电动牙刷', '9', '3'), ('92', '电吹风', '9', '3'), ('93', '美容仪', '9', '3'), ('94', '洁面仪', '9', '3'), ('95', '按摩器', '9', '3'), ('96', '健康秤', '9', '3'), ('97', '卷/直发器', '9', '3'), ('98', '剃/脱毛器', '9', '3'), ('99', '理发器', '9', '3'), ('100', '足浴盆', '9', '3'), ('101', '足疗机', '9', '3'), ('102', '按摩椅', '9', '3'), ('103', '家庭影院', '10', '3'), ('104', 'KTV音响', '10', '3'), ('105', '迷你音响', '10', '3'), ('106', 'DVD', '10', '3'), ('107', '功放', '10', '3'), ('108', '回音壁', '10', '3'), ('109', '麦克风', '10', '3');
COMMIT;
-- ----------------------------
-- Table structure for `tb_order`
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`total_amount` double NOT NULL,
`state` varchar(255) NOT NULL,
`creation_time` varchar(255) NOT NULL,
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `tb_order_ibfk_uid` FOREIGN KEY (`uid`) REFERENCES `tb_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_order`
-- ----------------------------
BEGIN;
INSERT INTO `tb_order` VALUES ('1', '21000', '2', '2020-10-21', '1'), ('4', '12000', '1', '2020-10-21', '1'), ('5', '12000', '1', '2020-10-21', '1'), ('6', '12000', '1', '2020-10-21', '1');
COMMIT;
-- ----------------------------
-- Table structure for `tb_orderdetail`
-- ----------------------------
DROP TABLE IF EXISTS `tb_orderdetail`;
CREATE TABLE `tb_orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` double NOT NULL,
`count` int(11) NOT NULL,
`oid` int(11) DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `oid` (`oid`),
KEY `pid` (`pid`),
CONSTRAINT `tb_orderdetail_ibfk_oid` FOREIGN KEY (`oid`) REFERENCES `tb_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `tb_orderdetail_ibfk_pid` FOREIGN KEY (`pid`) REFERENCES `tb_product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_orderdetail`
-- ----------------------------
BEGIN;
INSERT INTO `tb_orderdetail` VALUES ('1', '9000', '1', '1', '1'), ('2', '12000', '1', '1', '2');
COMMIT;
-- ----------------------------
-- Table structure for `tb_product`
-- ----------------------------
DROP TABLE IF EXISTS `tb_product`;
CREATE TABLE `tb_product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`pubdate` varchar(255) NOT NULL,
`picture` varchar(255) NOT NULL,
`price` decimal(10,0) NOT NULL,
`star` int(11) NOT NULL DEFAULT '0',
`summary` varchar(255) NOT NULL,
`cid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `cid` (`cid`),
CONSTRAINT `tb_product_ibfk_cid` FOREIGN KEY (`cid`) REFERENCES `tb_category` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_product`
-- ----------------------------
BEGIN;
INSERT INTO `tb_product` VALUES ('1', '华为P40', '2020-01-01', 'prodimg/2020/10/15/4ed2c882-e832-4232-bbec-d572905881a7.gif', '9000', '5', '华为P40。。。。', '1'), ('2', '联想ThinkPad', '2020-10-20', 'prodimg', '12000', '5', '联想ThinkPad...', '1');
COMMIT;
-- ----------------------------
-- Table structure for `tb_user`
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`gender` varchar(255) NOT NULL,
`role` varchar(255) NOT NULL DEFAULT 'member',
`state` int(255) DEFAULT '0',
`code` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `tb_user`
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` VALUES ('1', 'future-weaver', '670b14728ad9902aecba32e22fa4f6bd', 'leaf_8742@163.com', 'm', 'member', '1', '6fe9ca26-4229-4df4-ac42-c7bf15ee4f39');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
导入依赖
原生方式
org.mybatis:mybatis:3.5.6
junit:junit:4.13.2
Spring Boot方式
dependencies
org.springframework.boot:spring-boot-starter-web
org.springframework.boot:spring-boot-starter
com.mysql:mysql-connector-j
com.alibaba:druid-spring-boot-starter:1.2.15
org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.1
build
xml
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
创建全局配置文件
原生方式
mybatis-config.xml
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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
Spring Boot方式
- application.properties
properties
mybatis.configuration.cache-enabled=false
mybatis.configuration.local-cache-scope=statement
mybatis.configuration.lazy-loading-enabled=true
mybatis.configuration.auto-mapping-behavior=full
mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-aliases-package=com.futureweaver.domain
logging.level.com.futureweaver=DEBUG
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
创建映射文件
UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<select id="queryAll" resultType="com.futureweaver.domain.User">
SELECT * FROM tb_user
</select>
</mapper>
创建映射接口
UserMapper.java
java
public interface UserMapper {
public List<User> queryAll();
}
编写测试代码
UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testQueryAll() {
List<User> users = mapper.queryAll();
System.out.println(users);
}
}
log4j
注意
Spring Boot方式无需配置
导入依赖
- pom.xml
xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
导入配置文件
- log4j.properties
properties
global.conversion.pattern=[%-5p] [%d] [%l] %n%m%n%n
# root
log4j.rootLogger=DEBUG,stdout
# ConsoleAppender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold=DEBUG
log4j.appender.stdout.Encoding=utf-8
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=${global.conversion.pattern}
xml问题
修改全局配置文件
mybatis-config.xml
xml
<mappers>
<mapper resource="UserMapper.xml"/>
<mapper resource="com/future/weaver/mapper/AddressMapper.xml"/>
</mappers>
创建映射文件
AddressMapper.xml
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="com.futureweaver.mapper.AddressMapper">
<select id="queryAll" resultType="com.futureweaver.domain.Address">
SELECT * FROM tb_address
</select>
</mapper>
创建映射接口
AddressMapper.java
java
public interface AddressMapper {
public List<Address> queryAll();
}
编写测试代码
AddressMapperTest.java
java
public class AddressMapperTest {
private AddressMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
mapper = sqlSession.getMapper(AddressMapper.class);
}
@Test
public void testQuery() {
List<Address> addresses = mapper.queryAll();
System.out.println(addresses);
}
}
出现错误解析
tex
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/future/weaver/mapper/AddressMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/future/weaver/mapper/UserMapper.xml
解决方案
- pom.xml
xml
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
全局配置详解
properties
能够导入其他的properties文件。目的是为了能够直接使用properties里面的配置项
- 创建 properties 文件
properties
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
db.username=root
db.password=root
- 导入 properties 配置文件
xml
<properties resource="druid.properties"/>
- 引用 properties 属性
xml
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
settings【重点】
能够修改mybatis默认的配置项
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true / false | true |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION / STATEMENT | SESSION |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true / false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J / LOG4J / LOG4J2 / JDK_LOGGING / COMMONS_LOGGING / STDOUT_LOGGING / NO_LOGGING | 未设置 |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true / false | false |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE / PARTIAL / FULL | PARTIAL |
xml
<settings>
<setting name="cacheEnabled" value="false"/>
<setting name="localCacheScope" value="STATEMENT"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="autoMappingBehavior" value="FULL"/>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases【重点】
能够自动扫描实体类,也就是通过SQL查询到的结果,自动创建的javabean
- 修改 mybatis-config.xml
xml
<typeAliases>
<package name="com.futureweaver.domain"/>
</typeAliases>
- 修改 UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<select id="queryAll" resultType="User">
SELECT * FROM tb_user
</select>
</mapper>
typeHandlers【扩展】【重点】
- 添加 Gender.java
java
public enum Gender {
Male("m", "male"),
Female("f", "female");
private String code;
private String value;
private static final Map<String, Gender> CODE_MAP = new HashMap<>();
private static final Map<String, Gender> VALUE_MAP = new HashMap<>();
static {
for (Gender retCode : values()) {
CODE_MAP.put(retCode.getCode(), retCode);
VALUE_MAP.put(retCode.getValue(), retCode);
}
}
Gender(String code, String value) {
this.code = code;
this.value = value;
}
public String getCode() {
return code;
}
public String getValue() {
return value;
}
public static Gender fromCode(String code) {
return CODE_MAP.get(code);
}
public static Gender fromValue(String value) {
return VALUE_MAP.get(value);
}
}
- 修改 User.java
java
public class User {
private String id;
private String username;
private String password;
private String email;
private Gender gender;
private String flag;
private String role;
private String code;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", gender='" + gender + '\'' +
", flag='" + flag + '\'' +
", role='" + role + '\'' +
", code='" + code + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
- 添加 GenderTypeHandler.java
java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class GenderTypeHandler extends BaseTypeHandler<Gender> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Gender parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.getCode());
}
@Override
public Gender getNullableResult(ResultSet rs, String columnName) throws SQLException {
return Gender.fromCode(rs.getString(columnName));
}
@Override
public Gender getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return Gender.fromCode(rs.getString(columnIndex));
}
@Override
public Gender getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return Gender.fromCode(cs.getString(columnIndex));
}
}
- 修改 mybatis-config.xml
xml
<typeHandlers>
<package name="com.futureweaver.typehandlers"/>
</typeHandlers>
plugins【重点】
- 修改 pom.xml
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
- mybatis-config.xml
xml
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
- 修改 UserMapperTest.java
java
@Test
public void testQueryAll() {
PageHelper.startPage(1, 10);
List<User> users = mapper.queryAll();
PageInfo<User> usersPageBean = new PageInfo<>(users);
System.out.println(usersPageBean);
}
environments
环境变量,能够配置多个,比如开发环境、生产环境、测试环境
- mybatis-config.xml
xml
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="beta">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://10.1.71.113:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="dev"/>
<property name="password" value="Q3PF9xzP9gat"/>
</dataSource>
</environment>
<environment id="product">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://www.future-weaver.com:3306/db_shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="shopping"/>
<property name="password" value="uJT3ni2XAjjG"/>
</dataSource>
</environment>
</environments>
mappers【重点】
能够自动扫描Mapper接口及xml文件
xml
<mappers>
<package name="com.futureweaver.mapper"/>
</mappers>
映射文件详解
select
单参数
- UserMapper.java
java
public interface UserMapper {
public User queryById(int id);
// ...
}
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<select id="queryById" resultType="User">
SELECT * FROM tb_user WHERE id=#{id}
</select>
<!-- ... -->
</mapper>
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testQueryById() {
User user = mapper.queryById(1);
System.out.println(user);
}
// ...
}
多参数
- UserMapper.java
java
public interface UserMapper {
public User queryByUsernameAndPassword(String username, String password);
// ...
}
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<select id="queryByUsernameAndPassword" resultType="User">
SELECT * FROM tb_user WHERE username=#{username} AND password=#{password}
</select>
<!-- ... -->
</mapper>
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testQueryByUsernameAndPassword() {
User user = mapper.queryByUsernameAndPassword("future-weaver", "670b14728ad9902aecba32e22fa4f6bd");
System.out.println(user);
}
// ...
}
- 提示
tex
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
- 解决
java
public interface UserMapper {
public User queryByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
// ...
}
insert
插入数据
- UserMapper.java
java
public interface UserMapper {
public int register(User user);
// ...
}
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<insert id="register">
INSERT INTO tb_user(username, password, email, gender)
VALUES(#{username}, #{password}, #{email}, #{gender})
</insert>
</mapper>
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testInsert() {
User user = new User();
user.setUsername("newuser");
user.setPassword("newpass");
user.setEmail("newemail@future-weaver.com");
user.setGender(Gender.Male);
int count = mapper.register(user);
System.out.println(count);
}
// ...
}
- 问题 1
UserMapper.java
中没写@Param
注解,UserMapper.xml
也能直接引用吗?
- 问题 2
MySQL中,数据为什么没有成功插入?
事务控制
手动提交
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testInsert() {
User user = new User();
user.setUsername("newuser");
user.setPassword("newpass");
user.setEmail("newemail@future-weaver.com");
user.setGender(Gender.Male);
int count = mapper.register(user);
sqlSession.commit();
System.out.println(count);
}
// ...
}
自动提交
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testInsert() {
User user = new User();
user.setUsername("newuser");
user.setPassword("newpass");
user.setEmail("newemail@future-weaver.com");
user.setGender(Gender.Male);
int count = mapper.register(user);
System.out.println(count);
}
// ...
}
主键回填
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<insert id="register">
<selectKey keyProperty="id" resultType="String" order="BEFORE">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO tb_user(username, password, email, gender)
VALUES(#{username}, #{password}, #{email}, #{gender})
</insert>
<!-- ... -->
</mapper>
update
- UserMapper.java
java
public interface UserMapper {
public int update(User user);
// ...
}
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<update id="update">
UPDATE tb_user SET
username=#{username},
password=#{password},
email=#{email},
gender=#{gender},
role=#{role},
code=#{code}
WHERE id=#{id}
</update>
<!-- ... -->
</mapper>
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testUpdate() {
User user = new User();
user.setId("1");
user.setUsername("updatename");
user.setPassword("newpass");
user.setEmail("updateemail@future-weaver.com");
user.setGender(Gender.Female);
user.setRole("member");
int count = mapper.update(user);
System.out.println(count);
}
// ...
}
delete
- UserMapper.java
java
public interface UserMapper {
public int deleteById(int id);
// ...
}
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<delete id="deleteById">
DELETE FROM tb_user WHERE id=#{id}
</delete>
<!-- ... -->
</mapper>
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testDeleteById() {
mapper.deleteById(1);
}
// ...
}
深入
resultType
返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合的范型,而不是集合类型。 resultType 和 resultMap 只能同时使用一个。
resultMap
resultMap
元素是 MyBatis 中最重要最强大的元素。resultMap
的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
对一
编写实体类
- Cart.java
java
public class Cart {
private Integer id;
private Double amount;
private Integer count;
private Integer uid;
private Integer pid;
private Product product;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
@Override
public String toString() {
return "Cart{" +
"id=" + id +
", amount=" + amount +
", count=" + count +
", uid=" + uid +
", pid=" + pid +
", product=" + product +
'}';
}
}
- Product.java
java
public class Product {
private Integer id;
private String name;
private Date pubdate;
private Double price;
private Integer star;
private String summary;
private Integer cid;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getPubdate() {
return pubdate;
}
public void setPubdate(Date pubdate) {
this.pubdate = pubdate;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getStar() {
return star;
}
public void setStar(Integer star) {
this.star = star;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", pubdate=" + pubdate +
", price=" + price +
", star=" + star +
", summary='" + summary + '\'' +
", cid=" + cid +
'}';
}
}
编写Mapper
- CartMapper.java
java
public interface CartMapper {
public Cart queryById(Integer id);
}
- ProductMapper.java
java
public interface ProductMapper {
public Product queryById(Integer id);
}
编写映射文件
- CartMapper.xml
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="com.futureweaver.mapper.CartMapper">
<resultMap id="cartResultMap" type="Cart">
<id property="id" column="ctid"/>
<association property="product" resultMap="com.futureweaver.mapper.ProductMapper.productResultMap"/>
</resultMap>
<select id="queryById" resultMap="cartResultMap">
SELECT *, tb_cart.id AS ctid, tb_product.id AS pid
FROM tb_cart
INNER JOIN tb_product ON tb_cart.pid=tb_product.id
WHERE tb_cart.id=#{id}
</select>
</mapper>
- ProductMapper.xml
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="com.futureweaver.mapper.ProductMapper">
<resultMap id="productResultMap" type="Product">
<id property="id" column="pid"/>
</resultMap>
<select id="queryById" resultMap="productResultMap">
SELECT *, tb_product.id AS pid
FROM tb_product
WHERE tb_product.id=#{id}
</select>
</mapper>
编写单元测试
- CartMapperTest.java
java
public class CartMapperTest {
private CartMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(CartMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testQueryById() {
Cart cart = mapper.queryById(1);
System.out.println(cart);
}
}
- ProductMapperTest.java
java
public class ProductMapperTest {
private ProductMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(ProductMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testQueryById() {
Product product = mapper.queryById(1);
System.out.println(product);
}
}
对多
编写实体类
- User.java
java
public class User {
private Integer id;
private String username;
private String password;
private String email;
private Gender gender;
private String flag;
private String role;
private String code;
private List<Cart> carts;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", flag='" + flag + '\'' +
", role='" + role + '\'' +
", code='" + code + '\'' +
", carts=" + carts +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<Cart> getCarts() {
return carts;
}
public void setCarts(List<Cart> carts) {
this.carts = carts;
}
}
编写Mapper
- UserMapper.java
java
public interface UserMapper {
public User queryById(Integer id);
// ...
}
编写映射文件
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="uid"/>
<collection property="carts" resultMap="com.futureweaver.mapper.CartMapper.cartResultMap"/>
</resultMap>
<select id="queryById" resultMap="userResultMap">
SELECT *, tb_user.id AS uid, tb_cart.id AS ctid, tb_product.id AS pid
FROM tb_user
INNER JOIN tb_cart ON tb_cart.uid=tb_user.id
INNER JOIN tb_product ON tb_cart.pid=tb_product.id
WHERE tb_user.id=#{id}
</select>
<!-- ... -->
</mapper>
编写单元测试
- UserMapperTest.java
java
public class UserMapperTest {
private UserMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testQueryById() {
User user = mapper.queryById(1);
System.out.println(user);
}
// ...
}
延迟加载
编写Mapper
- CartMapper.java
java
public interface CartMapper {
public Cart queryById(Integer id);
public List<Cart> queryByUid(Integer uid);
}
编写映射文件
- CartMapper.xml
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="com.futureweaver.mapper.CartMapper">
<resultMap id="cartResultMap" type="Cart">
<id property="id" column="ctid"/>
<association property="product" resultMap="com.futureweaver.mapper.ProductMapper.productResultMap"/>
</resultMap>
<select id="queryByUid" resultMap="cartResultMap">
SELECT *, tb_cart.id AS ctid
FROM tb_cart
WHERE tb_cart.uid=#{uid}
</select>
<!-- ... -->
</mapper>
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="uid"/>
<collection property="carts"
column="uid"
select="com.futureweaver.mapper.CartMapper.queryByUid"/>
</resultMap>
<select id="queryById" resultMap="userResultMap">
SELECT *, tb_user.id AS uid
FROM tb_user
WHERE tb_user.id=#{id}
</select>
<!-- ... -->
</mapper>
编写全局配置
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>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<!-- ... -->
</configuration>
动态SQL
if
编写Mapper
- ProductMapper.java
java
public interface ProductMapper {
public List<Product> search(@Param("word") String word,
@Param("minPrice") Double minPrice,
@Param("maxPrice") Double maxPrice);
// ...
}
编写映射文件
- ProductMapper.xml
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="com.futureweaver.mapper.ProductMapper">
<resultMap id="productResultMap" type="Product">
<id property="id" column="pid"/>
</resultMap>
<select id="search" resultMap="productResultMap">
SELECT *, tb_product.id AS pid
FROM tb_product
WHERE
<if test="word != null">
name LIKE concat('%', #{word}, '%') AND
</if>
<if test="minPrice != null">
price >= #{minPrice} AND
</if>
<if test="maxPrice != null">
price <= #{maxPrice} AND
</if>
0=0
</select>
<!-- ... -->
</mapper>
编写单元测试
- UserMapperTest.java
java
package com.futureweaver.mapper;
import com.futureweaver.domain.Product;
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.List;
public class ProductMapperTest {
private ProductMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(ProductMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testSearch() {
List<Product> products = mapper.search("ThinkPad", null, null);
System.out.println(products);
}
// ...
}
choose-when
编写映射文件
xml
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
where
编写映射文件
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="com.futureweaver.mapper.ProductMapper">
<resultMap id="productResultMap" type="Product">
<id property="id" column="pid"/>
</resultMap>
<select id="search" resultMap="productResultMap">
SELECT *, tb_product.id AS pid
FROM tb_product
<where>
<if test="word != null">
AND name LIKE concat('%', #{word}, '%')
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
<if test="maxPrice != null">
AND price <= #{maxPrice}
</if>
</where>
</select>
<!-- ... -->
</mapper>
set
编写实体类
- Order.java
java
public class Order {
private Integer id;
private Double totalAmount;
private String state;
private Date creationTime;
private Integer uid;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", totalAmount=" + totalAmount +
", state='" + state + '\'' +
", creationTime=" + creationTime +
", uid=" + uid +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(Double totalAmount) {
this.totalAmount = totalAmount;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Date getCreationTime() {
return creationTime;
}
public void setCreationTime(Date creationTime) {
this.creationTime = creationTime;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}
编写Mapper
- OrderMapper.java
java
public interface OrderMapper {
public int update(Order order);
}
编写映射文件
- OrderMapper.xml
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="com.futureweaver.mapper.OrderMapper">
<update id="update">
UPDATE tb_order
<set>
<if test="totalAmount != null">
total_amount = #{totalAmount},
</if>
<if test="state != null">
state = #{state},
</if>
<if test="creationTime != null">
creation_time = #{creationTime},
</if>
<if test="uid != null">
uid = #{uid}
</if>
</set>
WHERE id=#{id}
</update>
</mapper>
编写单元测试
- OrderMapperTest.java
java
package com.futureweaver.mapper;
import com.futureweaver.domain.Cart;
import com.futureweaver.domain.Order;
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;
public class OrderMapperTest {
private OrderMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(OrderMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testUpdate() {
Order order = new Order();
order.setId(1);
order.setState("3");
int update = mapper.update(order);
System.out.println(update);
}
}
foreach
编写Mapper
- CartMapper.java
java
public interface CartMapper {
public int delete(@Param("ids") List<Integer> ids);
}
编写映射文件
- CartMapper.xml
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="com.futureweaver.mapper.CartMapper">
<delete id="delete">
DELETE FROM tb_cart WHERE id IN
<foreach collection="ids" item="item"
open="(" separator="," close=")">
#{item}
</foreach>
</delete>
<!-- ... -->
</mapper>
编写单元测试
- CartMapperTest.java
java
package com.futureweaver.mapper;
import com.futureweaver.domain.Cart;
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.Arrays;
import java.util.List;
public class CartMapperTest {
private CartMapper mapper;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
mapper = sqlSession.getMapper(CartMapper.class);
}
@After
public void destroy() {
sqlSession.close();
}
@Test
public void testDelete() {
Integer[] ids = {1, 2};
List<Integer> integers = Arrays.asList(ids);
mapper.delete(integers);
}
// ...
}
trim
xml
<insert id="addCheckRequest" parameterType="java.lang.String">
INSERT INTO check_request
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="register_id!=null and register_id!='' ">
register_id,
</if>
<if test="medical_technology_id!=null and medical_technology_id!='' ">
medical_technology_id,
</if>
<if test="check_info!=null and check_info!='' ">
check_info,
</if>
<if test="check_position!=null and check_position!='' ">
check_position,
</if>
<if test="check_remark!=null and check_remark!='' ">
check_remark,
</if>
creation_time,check_state
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="register_id!=null and register_id!='' ">
#{register_id,jdbcType=VARCHAR},
</if>
<if test="medical_technology_id!=null and medical_technology_id!='' ">
#{medical_technology_id,jdbcType=VARCHAR},
</if>
<if test="check_info!=null and check_info!='' ">
#{check_info,jdbcType=VARCHAR},
</if>
<if test="check_position!=null and check_position!='' ">
#{check_position,jdbcType=VARCHAR},
</if>
<if test="check_remark!=null and check_remark!='' ">
#{check_remark,jdbcType=VARCHAR},
</if>
now(),"已开立"
</trim>
</insert>
SQL模板
sql
、include
编写映射文件
- UserMapper.xml
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="com.futureweaver.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="uid"/>
<collection property="carts"
column="uid"
select="com.futureweaver.mapper.CartMapper.queryByUid"/>
</resultMap>
<sql id="userFieldList">
tb_user.id AS uid,
username,
password,
email,
gender,
role,
state,
code
</sql>
<select id="queryById" resultMap="userResultMap">
SELECT <include refid="userFieldList"/>
FROM tb_user
WHERE tb_user.id=#{id}
</select>
</mapper>
补充
Druid集成
概念
Druid 是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和 SQL 解析器组成。该项目主要是为了扩展 JDBC 的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计 SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。
不同连接池对比
测试执行申请归还连接 1,000,000(一百万)次总耗时性能对比。
测试环境
环境 | 版本 |
---|---|
OS | OS X 10.8.2 |
CPU | Intel i7 2GHz 4 Core |
JVM | Java Version 1.7.0_05 |
基准测试结果对比
JDBC-Conn Pool | 1 Thread | 2 threads | 5 threads | 10 threads | 20 threads | 50 threads |
---|---|---|---|---|---|---|
Druid | 898 | 1,191 | 1,324 | 1,362 | 1,325 | 1,459 |
tomcat-jdbc | 1,269 | 1,378 | 2,029 | 2,103 | 1,879 | 2,025 |
DBCP | 2,324 | 5,055 | 5,446 | 5,471 | 5,524 | 5,415 |
BoneCP | 3,738 | 3,150 | 3,194 | 5,681 | 11,018 | 23,125 |
jboss-datasource | 4,377 | 2,988 | 3,680 | 3,980 | 32,708 | 37,742 |
C3P0 | 10,841 | 13,637 | 10,682 | 11,055 | 14,497 | 20,351 |
Proxool | 16,337 | 16,187 | 18,310(Ex) | 25,945 | 33,706(Ex) | 39,501 (Ex) |
测试结论
- Druid 是性能最好的数据库连接池,tomcat-jdbc 和 druid 性能接近。
- Proxool 在激烈并发时会抛异常,不适用。
- C3P0 和 Proxool 都相当慢,影响 sql 执行效率。
- BoneCP 性能并不优越,采用 LinkedTransferQueue 并没有能够获得性能提升。
- 除了 bonecp,其他的在 JDK 7 上跑得比 JDK 6 上快。
- jboss-datasource 虽然稳定,但性能很糟糕。
配置pom.xml
引入Druid依赖
xml
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
创建DruidDataSourceFactory
MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。
java
package com.futureweaver.mybatis.part2.utils;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
public MyDruidDataSourceFactory() {
this.dataSource = new DruidDataSource();//替换数据源
}
}
修改mybatis-config.xml
mybatis-config.xml中连接池相关配置。
xml
<!--连接池-->
<dataSource type="com.futureweaver.mybatis.part2.utils.DruidDataSourceFactory"><!--数据源工厂-->
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
注意:< property name="属性名" />属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource中一致。
注解
通过在接口中直接添加MyBatis注解,完成CRUD。
- 注意:接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xml的< mappers >中。
- 经验:注解模式属于硬编码到.java文件中,失去了使用配置文件外部修改的优势,可结合需求选用。
xml
<mappers>
<mapper class="com.futureweaver.mybatis.part1.annotations.UserMapper" /><!-- class="接口全限定名"-->
</mappers>
查询
java
public interface UserMapper {
@Select("SELECT * FROM t_users WHERE id = #{id}")
public User selectUserById(Integer id);
@Select("SELECT * FROM t_users WHERE id = #{id} AND password = #{pwd}")
public User selectUserByIdAndPwd_annotation(@Param("id") Integer id, @Param("pwd") String password);
}
删除
java
@Delete(value = "DELETE FROM t_users WHERE id = #{id}")
public int deleteUser(Integer id);
修改
java
@Update("UPDATE t_users SET name = #{name} , password = #{password} , salary = #{salary} , birthday = #{birthday} WHERE id = #{id}")
public int updateUser(User user);
插入
java
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUser(User user);
@Options(useGeneratedKeys = true , keyProperty = "id") // 自增key,主键为id
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUserGeneratedKeys(User user);
缓存
一级缓存
SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。
关闭一级缓存
xml
<configuration>
<properties .../>
<!-- 注意书写位置 -->
<settings>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
<typeAliases></typeAliases>
</configuration>
二级缓存
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。
关闭二级缓存
< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。
xml
<configuration>
<properties .../>
<!-- 注意书写位置 -->
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
<typeAliases></typeAliases>
</configuration>
指定Mapper缓存
xml
<mapper namespace="com.futureweaver.mybatis.part2.cache.BookDao">
<cache /> <!-- 指定缓存 -->
<select id="selectBookByCondition" resultType="com.futureweaver.mybatis.part2.cache.Book">
SELECT * FROM t_books
</select>
</mapper>
java
@Test
public void testMapperCache(){
SqlSession sqlSession1 = MyBatisUtils.getSession();
BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);
bookDao1.selectBookByCondition(new Book());
sqlSession1.close(); //必须关闭SqlSession才可缓存数据
//--------------------
SqlSession sqlSession2 = MyBatisUtils.getSession();
BookDao bookDao2 = sqlSession2.getMapper(BookDao.class);
bookDao2.selectBookByCondition(new Book());
sqlSession2.close(); //缓存击中
}
缓存清空并重新缓存
java
@Test
public void testMapperCache(){
SqlSession sqlSession1 = MyBatisUtils.getSession();
BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);
bookDao1.selectBookByCondition(new Book());
sqlSession1.close(); //必须关闭SqlSession才可缓存数据
//--------------------
SqlSession sqlSession3 = MyBatisUtils.getSession();
BookDao bookDao3 = sqlSession3.getMapper(BookDao.class);
bookDao3.deleteBookById(102);
sqlSession3.commit(); //DML成功,数据发生变化,缓存清空
sqlSession3.close();
//--------------------
SqlSession sqlSession2 = MyBatisUtils.getSession();
BookDao bookDao2 = sqlSession2.getMapper(BookDao.class);
bookDao2.selectBookByCondition(new Book());
sqlSession2.close(); //缓存未击中,重新查询数据库、重新缓存
}
$
与#
${attribute} 属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题。
$符号参数绑定
java
public List<User> selectAllUsers1(User user); // ${name} ${id} 可获取user中的属性值
public List<User> selectAllUsers2(@Param("rule") String rule); //必须使用@Param否则会作为属性解析
xml
<select id="selectAllUsers1" resultType="user">
SELECT * FROM t_users
WHERE name = '${name}' or id = ${id} <!-- 拼接name和id,如果是字符类型需要用单引号:'${name}' -->
</select>
<select id="selectAllUsers2" resultType="user">
SELECT * FROM t_users
ORDER BY id ${rule} <!-- 拼接 asc | desc -->
</select>
java
User user = new User(....);
List<User> ulist1 = userDAO.selectAllUsers1(user); //调用时传入user对象
List<User> ulist2 = userDao.selectAllUsers2("desc"); //调用时传入asc | desc
$符号注入攻击
xml
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_user
WHERE name = '${name}' <!-- 会存在注入攻击 比如传入参数是 【String name = "tom' or '1'='1";】-->
</select>
Idea模板
- 进入 Idea 欢迎页面(如果已开启某个项目,点击菜单栏 ->
File
->Close Project
) - 左侧导航栏,选择
Customize
,选择All Settings...
- 左侧导航栏,选择
Editor
->File And Code Templates
pom.xml
- 右侧视图,选择
Other
选项卡 - 右侧窗口中的左侧导航栏,选择
Maven
->Maven Project.xml
- 将以下文本,全部 CV 替换
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
#if (${HAS_PARENT})
<parent>
<groupId>${PARENT_GROUP_ID}</groupId>
<artifactId>${PARENT_ARTIFACT_ID}</artifactId>
<version>${PARENT_VERSION}</version>
#if (${HAS_RELATIVE_PATH})
<relativePath>${PARENT_RELATIVE_PATH}</relativePath>
#end
</parent>
#end
<groupId>${GROUP_ID}</groupId>
<artifactId>${ARTIFACT_ID}</artifactId>
<version>${VERSION}</version>
<dependencies>
${END}
<!-- log4j -->
<!-- mybatis -->
<!-- junit -->
<!-- c3p0 -->
<!-- druid -->
<!-- spring-jdbc -->
<!-- commons-dbutils -->
<!-- commons-beanutils -->
<!-- commons-io -->
<!-- fastjson -->
<!-- jackson-databind -->
<!-- jakarta.servlet-api -->
<!-- mysql-connector-j -->
<!-- jakarta.servlet.jsp.jstl-api -->
<!-- jakarta.servlet.jsp.jstl -->
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
druid.properties
- 右侧视图,选择
Files
选项卡- 点击
+
Name
->druid-properties
Extention
->properties
File Name
->druid.properties
- 将以下内容,粘贴到模板内部
properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${db_name}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username=${db_username}
password=${db_password}
## 初始化connection数量
initialSize=10
## 最大连接数量
maxActive=50
## 最小空闲数量
minIdle=5
## 等待时间
maxWait=5000
log4j.properties
- 右侧视图,选择
Files
选项卡- 点击
+
Name
->log4j-properties
Extention
->properties
File Name
->log4j.properties
- 将以下内容,粘贴到模板内部
properties
global.conversion.pattern=[%-5p] [%d] [%l] %n%m%n%n
# root
log4j.rootLogger=DEBUG,stdout
# ConsoleAppender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold=DEBUG
log4j.appender.stdout.Encoding=utf-8
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=${global.conversion.pattern}
mybatis-config.xml
- 右侧视图,选择
Files
选项卡- 点击
+
Name
->mybatis-config-xml
Extention
->xml
File Name
->mybatis-config.xml
- 将以下内容,粘贴到模板内部
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>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="autoMappingBehavior" value="FULL"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="cacheEnabled" value="false"/>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
<typeAliases>
<package name="${type_aliases_package}"/>
</typeAliases>
<!--
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="${mybatis_datasource_factory}">
</dataSource>
</environment>
</environments>
<mappers>
<package name="${mappers_package}"/>
</mappers>
</configuration>
mybatis-mapper.xml
- 右侧视图,选择
Files
选项卡- 点击
+
Name
->mybatis-mapper-xml
Extention
->xml
- 将以下内容,粘贴到模板内部
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="${PACKAGE_NAME}.${NAME}">
</mapper>
MyBatisDataSourceFactory.java
- 右侧视图,选择
Files
选项卡- 点击
+
Name
->MyBatisDataSourceFactory.java
Extention
->java
File Name
->MyBatisDataSourceFactory.java
- 将以下内容,粘贴到模板内部
java
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.io.Resources;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class MyBatisDataSourceFactory extends PooledDataSourceFactory {
public MyBatisDataSourceFactory() {
try {
InputStream is = Resources.getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(is);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
课程目标总结
- 能够了解什么是框架
- 掌握 Mybatis 框架开发快速入门
- 掌握 Mybatis 框架的基本 CRUD 操作
- 掌握 Mybatis 的参数深入
- 掌握 Mybatis 的 DAO 层开发
- 掌握 SqlMapConfig.xml 配置文件
- 掌握 Mybatis 动态 SQL
- 掌握 Mybatis 多表关联查询
- 掌握 Mybatis 的延迟加载
- 掌握 Mybatis 缓存