1. 环境配置
1.1 引入依赖或直接添加jar包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
1.2 项目目录结构
1.3 数据表创建
-- ----------------------------
-- Table structure for `account`
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) NOT NULL,
`money` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', '1', '1000');
INSERT INTO `account` VALUES ('2', '1', '2000');
INSERT INTO `account` VALUES ('3', '2', '2000');
INSERT INTO `account` VALUES ('4', '3', '30');
-- ----------------------------
-- Table structure for `role`
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', '狗头军师');
INSERT INTO `role` VALUES ('2', '千年咸鱼');
INSERT INTO `role` VALUES ('3', '吃瓜群众');
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '18');
INSERT INTO `user` VALUES ('2', '李四', '21');
INSERT INTO `user` VALUES ('3', '张待遇', '30');
INSERT INTO `user` VALUES ('4', '张哈哈', '12');
INSERT INTO `user` VALUES ('5', '张大炮', '30');
-- ----------------------------
-- Table structure for `user_role`
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '1', '2');
INSERT INTO `user_role` VALUES ('3', '1', '3');
INSERT INTO `user_role` VALUES ('4', '2', '1');
INSERT INTO `user_role` VALUES ('5', '2', '2');
INSERT INTO `user_role` VALUES ('6', '3', '1');
INSERT INTO `user_role` VALUES ('7', '5', '2');
1.4 日志配置
- 把log4j.properties文件添加到resources根目录下即可,控制台会打印mybatis的sql语句
# LOGFILE把日志记录在文件中
#log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.rootCategory=debug, CONSOLE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# 如果上面rootCategory启用了LOGFILE,需要把下面注释解开才能把日志记录在文件中
#log4j.appender.LOGFILE=org.apache.log4j.FileAppender
#log4j.appender.LOGFILE.File=d:\axis.log
#log4j.appender.LOGFILE.Append=true
#log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
#log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
2. 入门配置
2.1 实体类
package com.domain;
public class User {
private Integer id;
private String username;
private Integer age;
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}
2.2 Dao接口
package com.dao;
import com.domain.User;
import java.util.List;
public interface IUserDao {
List<User> findAll();
User findById(int id);
}
2.3 Dao映射文件
- 映射文件要跟Dao文件在同一目录下
- 映射文件的目录创建时只能只能只能一次创建一层文件夹,不能跟包一样带点.创建多层,否则半天都找不到运行错误的原因
- 建议把包设置分层显示,避免看错文件夹层次
<?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指向的是Dao接口 -->
<mapper namespace="com.dao.IUserDao">
<!-- 该方法虽然返回的是List<User>,但这里还是要填写实体类User,而不是List -->
<select id="findAll" resultType="com.domain.User">
select * from user;
</select>
<!-- select标签id属性要跟Dao接口中的方法名一样 -->
<!-- parameterType是方法参数类型,该属性写不写都可以 -->
<!-- resultType是返回值类型 -->
<!-- parameterType和resultType默认都要带上包名,但可以通过主配置文件设置别名 -->
<!-- int,String,date等常用类型mybatis已经默认设置了别名,而且不区分大小写 -->
<select id="findById" parameterType="iNt" resultType="com.domain.User">
<!-- 如果方法参数是单个基本类型值,则可用 #{随便一个名字} 作为占位符,作用等同于?占位符 -->
select * from user where id=#{userId}
</select>
</mapper>
2.4 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="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis03"/>
<property name="username" value="root"/>
<property name="password" value="root"></property>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 指定映射文件存放的包 -->
<package name="com.dao"/>
<!--
指定单个映射文件
这两个mapper作用一样的,dao接口名字要跟映射文件名字相同
<mapper class="com.dao.IUserDao"/>
作用同上
<mapper resource="com/dao/IUserDao.xml"/>
-->
</mappers>
</configuration>
2.5 测试
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis.xml");
// 2.创建 SqlSessionFactory 的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 3.使用构建者创建工厂对象
SqlSessionFactory factory = builder.build(in);
// 4.使用 SqlSessionFactory 生产 SqlSession 对象
SqlSession session = factory.openSession();
// 5.使用 SqlSession 创建 dao 接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
// 6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
User user=userDao.findById(1);
System.out.println("findById:"+user);
// 7.释放资源
session.close();
in.close();
3. 方法参数与返回值深入理解
3.1 单个基本类型作为方法参数
-
#{名字}
是作为参数的占位符,如果方法参数只有一个,并且该参数为基本类型,则名字随意起,不用跟方法参数的形参名字一样,等同一个sql占位符?,防sql注入,但实际中尽量还是跟方法参数名字一致,方便阅读 -
${value}
固定写法,名字必须是value,是作为一个变量跟sql语句直接拼接字符串,可能会出现sql注入问题
<!-- 接口方法User findById(int id) -->
<select id="findById" parameterType="iNt" resultType="com.domain.User" >
<!-- #{}名字可以不跟方法参数名一样 -->
select * from user where id=#{userId}
<!-- 等同于"select * from user where id="+id -->
select * from user where id=${value}
</select>
3.2 使用多个基本类型作为方法参数
- xml方式:
- 使用@Param注解设置占位符的名称(推荐)
- 使用#{下标}参数下标作为占位符(不要使用,方法参数修改导致影响大,而且暂时测试不成功)
// 1.使用@Param("参数名")设置占位符的名称,一般跟方法参数名一致
List<User> findByAge(@Param("minAge") int minAge,@Param("maxAge") int maxAge);
// 2.在xml的sql中直接使用@Param定义的参数名作为占位
// 多个参数时select标签不用设置parameterType属性
<select id="findByAge" resultType="com.domain.User">
select * from user where age between #{minAge} and #{maxAge}
</select>
- 注解方式:直接根据方法参数名作为占位符名字
// #{param1}或#{id}为第一个占位符
// #{param2}或#{count}为第二个占位符
@Update("update tb_template set spec_num=spec_num+#{count} where id=#{id}")
public void updateSpecNum(int id, int count);
3.3 使用Map作为方法参数
List<User> findByMap(Map<String,Object> map);
<!-- #{key}占位符名称对应Map的key -->
<select id="findByMap" parameterType="java.util.Map" resultType="com.domain.User">
select * from user where username like #{username} and age=#{age}
</select>
Map<String,Object> map=new HashMap<String, Object>();
map.put("username","%张%");
map.put("age",30);
List<User> userList = userDao.findByMap(map);
for (User user : userList) {
System.out.println(user);
}
3.4 使用对象作为方法参数
- 对象可以是实体类也可以自己定义的pojo类(带getter和setter方法)
- #{类属性名}获取类属性值作为sql参数的值,如果类属性是引用类型,则可以通过
.
来获取该引用类型属性的属性
自定义条件类
package com.domain;
public class UserCondition {
//基本类型
private int minAge;
private int maxAge;
//引用类型
private User user;
public int getMinAge() {
return minAge;
}
public void setMinAge(int minAge) {
this.minAge = minAge;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
映射文件
<!-- 使用条件对象作为方法参数 -->
<select id="findByCondition" parameterType="com.domain.UserCondition" resultType="com.domain.User">
<!-- CDATA是用来忽略sql语句中在xml中的特殊符号 -->
<!-- #{类属性名}获取类属性值作为sql参数的值 -->
<![CDATA[
select * from user where username like #{user.username} and age>#{minAge} and age<#{maxAge};
]]>
</select>
测试
User user=new User();
user.setUsername("%张%");
UserCondition userCondition = new UserCondition();
userCondition.setMinAge(10);
userCondition.setMaxAge(20);
userCondition.setUser(user);
List<User> list=userDao.findByCondition(userCondition);
for (User u : list) {
System.out.println(u);
}
3.5 使用resultType指定返回值类型
- resultType可以指定基本类型和引用类型的返回值
- 例子看上面
3.6 使用resultMap指定查询结果的映射规则
- id标签是主键列,result标签是普通列
- association标签和collection标签用于配置一对多关系,详细说明看后面
- property表示类的的属性,column表示列名
- jdbcType表示类的的属性的类型,不常用,值只能是org.apache.ibatis.type.JdbcType中的枚举值
<!-- 定义查询结果映射类对象规则 -->
<!-- id表示唯一的名字,被其他select标签引用 -->
<!-- type表示需要映射的类 -->
<resultMap id="userMap" type="com.domain.User">
<!-- 主键列 -->
<id property="id" column="id" ></id>
<!-- 把username列映射进User类的uname属性中 -->
<result property="uname" column="username" ></result>
<!-- 如果不指定映射关系的列和属性,则默认规则进行映射 -->
<!-- 这条映射规则可以不用写,因为mybatis默认按名字和自身的类型进行映射 -->
<result property="age" column="age" jdbcType="INTEGER"></result>
</resultMap>
<!-- 使用自定义查询结果封装规则作为返回值 -->
<select id="findAll" resultMap="userMap">
select * from user;
</select>
</resultMap>
4.映射文件其他说明
4.1 插入数据获取主键值
<insert id="save" parameterType="com.domain.User">
<!-- keyProperty主键属性名,keyColumn主键列名,resultType返回值类型 -->
<selectKey keyProperty="id" keyColumn="id" resultType="int">
<!-- 查询最近插入的数据的主键值(mysql可用,oracle不可用,oracle可以查询序列获取主键值)-->
select last_insert_id();
</selectKey>
insert into user values(null,#{username},#{age})
</insert>
4.2 定义sql片段重复引用
<!-- 定义sql片段 -->
<!-- 注意别引用分号;结尾 -->
<sql id="defaultSql">
select * from user
</sql>
<select id="findById" parameterType="int" resultType="user" >
<!-- 引用sql片段 -->
<include refid="defaultSql"></include>
where id=#{userId}
</select>
5. mybatis主配置文件
5.1 配置项和顺序
-properties(属性)
--property
-settings(全局配置参数)
--setting
-typeAliases(类型别名)
--typeAliase
--package
-typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
--environment(环境子属性对象)
---transactionManager(事务管理)
---dataSource(数据源)
-mappers(映射器)
--mapper
--package
5.2 properties(属性变量)
- 在properties标签内使用property标签定义属性变量
<properties>
<property name="db.username" value="root"/>
<property name="db.password" value="root"/>
</properties>
- 在外部properties文件中定义属性变量
//db.properties文件内容
db.username=root
db.password=root
<!-- 引入外部属性文件 -->
<properties resource="db.properties"> </properties>
- 使用${}调用属性变量
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis03"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"></property>
</dataSource>
</environment>
</environments>
5.3 typeAliases(类型别名)
- 定义别名
<typeAliases>
<!-- 定义单个类的别名 -->
<typeAlias alias="User" type="com.domain.User"></typeAlias>
<!-- 定义指定包下所有类的别名,别名为类名,不区分大小写 -->
<package name="com.domain"/>
</typeAliases>
- parameterType与resultType都可以使用别名
<!-- com.domain.User别名变成了user -->
<select id="findById" parameterType="iNt" resultType="user" >
select * from user where id=#{userId}
</select>
5.4 全局配置参数settings
- 主要用于延迟加载和缓存参数配置,详情查看后面,这里不概述
5.5 映射器mappers
<mappers>
<!-- 指定映射文件存放的包 -->
<package name="com.dao"/>
<!--
指定单个映射文件
这两个mapper作用一样的,dao接口名字要跟映射文件名字相同
<mapper class="com.dao.IUserDao"/>
作用同上
<mapper resource="com/dao/IUserDao.xml"/>
-->
</mappers>
5.6 插件plugins
- 参考7.2
6. 使用if/where/foreach标签编写动态sql
6.1 if标签
- test是条件表达式,可用ONGL表达式
<select id="findByUser" parameterType="user" resultType="user">
select * from user where 1=1
<if test="username!=null and !username.trim().equals('')">
and username like #{username}
</if>
<if test="age!=null">
and age>#{age}
</if>
</select>
6.2 where标签
- where标签会自动添加where关键字,并且删除where关键字相邻的and (等同where关键字替换了第一个and)
<!-- select * from user WHERE username like ? and age>? -->
<select id="findByUser" parameterType="user" resultType="user">
select * from user
<where>
<if test="username!=null and !username.trim().equals('')">
and username like #{username}
</if>
<if test="age!=null">
and age>#{age}
</if>
</where>
</select>
6.3 foreach
- 定义个类,设置一个集合属性
package com.domain;
import java.util.List;
public class QueryObject {
private List<Integer> ageList;
public List<Integer> getAgeList() {
return ageList;
}
public void setAgeList(List<Integer> ageList) {
this.ageList = ageList;
}
}
- foreach使用
<!-- select * from USER where age in ( ? , ? , ? ) -->
<select id="findByAge" resultType="user">
select * from USER where age in
<!-- collection的ageList对应QueryObject类的ageList,不用加#{} -->
<!-- item表示遍历时每个对象的别名 -->
<!-- open表示遍历前的前置字符,close表示遍历后的后置字符 -->
<!-- separator表示集合每个元素用指定分隔符进行隔开 -->
<foreach collection="ageList" item="age" open="(" close=")" separator=",">
#{age}
</foreach>
</select>
- 测试
List<Integer> ageList=new ArrayList<Integer>();
ageList.add(20);
ageList.add(12);
ageList.add(30);
QueryObject queryObject = new QueryObject();
queryObject.setAgeList(ageList);
List<User> list = userDao.findByAge(queryObject);
for (User u : list) {
System.out.println(u);
}
7. mybatis集成PageHelper分页插件
7.1 引入PageHelper依赖
<!-- 引入分页插件依赖 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
7.2 在mybatis主配置文件中添加PageHelper插件
- 注意plugins标签在mybatis主配置中的位置顺序,参考上面5.1
<!-- 注意plugins的位置与顺序 -->
<plugins>
<plugin interceptor ="com.github.pagehelper.PageInterceptor">
<!-- 这里可以设置多个属性和值 -->
<property name="reasonable" value="true"></property>
</plugin>
</plugins>
7.3 PageHelper与PageInfo的方法使用
// PageHelper.startPage()方法必须在调用Dao方法前使用才能成功分页
// 第一个参数是当前页码,第二个参数是每页显示的记录数
PageHelper.startPage(2,2);
List<User> userList = userDao.findAll();
for (User user : userList) {
System.out.println(user);
}
// 创建PageInfo对象并把查询结果集合数据存放进去
PageInfo<User> pageInfo=new PageInfo<User>(userList);
System.out.println("总记录数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示的记录数:"+pageInfo.getPageSize());
System.out.println("获取上一页页码:"+pageInfo.getPrePage());
System.out.println("获取下一页页码:"+pageInfo.getNextPage());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
System.out.println("是否还有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("是否还有下一页:"+pageInfo.isHasNextPage());
// 从PageInfo中获取对象集合数据
List<User> list = pageInfo.getList();
7.4 SpringBoot使用PageHelper
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>