Mybatis 详解
简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
使用
①添加MyBatis的坐标
②创建user数据表 [默认已创建]
③编写User实体类 [默认已创建]
④编写映射文件UserMapper.xml
⑤编写核心文件Mybatis_config.xml
⑥编写测试类
Mybatis 坐标
<!--mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql驱动坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!--单元测试坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志坐标-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
数据库与实体类
SQL语句,可直接导入
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`uid` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`gender` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, '张三', '男');
INSERT INTO `users` VALUES (2, '李四', '男');
SET FOREIGN_KEY_CHECKS = 1;
User
实体类字段
public class User {
int id;
String name;
String gender;
}
Mybatis_config
将此配置文件放在resources
目录下
<?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">
<!-- 第一个环境,id处也可以是default-->
<environment id="development">
<!-- JDBC事务-->
<transactionManager type="JDBC"/>
<!-- 连接数据库配置-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/cms_hisoft?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="com/bright/Dao/mapper/userMapper.xml"/>
</mappers>
</configuration>
映射实现
创建接口
public interface User {
public List<User> selectAll();
}
创建userMapper.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="userMapper">
<select id="selectAll" resultType="com.bright.pojo.User">
select * from users
</select>
<insert id="insert" parameterType="com.bright.pojo.User">
insert into users values(#{id},#{name},#{gender})
</insert>
<update id="modify" parameterType="com.bright.pojo.User">
update users set name=#{name},gender=#{gender} where id=#{id};
</update>
<delete id="delete" parameterType="com.bright.pojo.User">
delete from users where id=#{id}
</delete>
</mapper>
增删改查测试
查询数据,不需要提交事务
public class MyBatisTest {
@Test
public void test1() throws IOException {
//获得核心配置文件
InputStream resourceAsStream= Resources.getResourceAsStream("Mybatis-config.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session回话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
List<User> userList = sqlSession.selectList("userMapper.selectAll");
//打印数据
System.out.println(userList);
//释放资源
sqlSession.close();
}
}
增删改,与查询数据不同的是增删改需要提交事务
插入数据
User user=new User("1","张三,"男");
sqlSession.insert("userMapper.insert",user);
sqlSession.commit();//提交事务
更新数据
User user=new User("1","李四","男");
sqlSession.update("userMapper.modify",user);
sqlSession.commit();//提交事务
删除数据
User user=new User();
user.setI(1);
sqlSession.delete("userMapper.delete",user);
sqlSession.commit();//提交事务
详解
执行流程
String resource = "org/mybatis/builder/mybatis-config.xml";
//获得核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取工厂构造器
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//获得session工厂对象
SqlSessionFactory factory = builder.build(inputStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行SQL Mapper
List<User> userList = sqlSession.selectList("userMapper.selectAll");
sqlSession.insert("userMapper.insert",user);
sqlSession.update("userMapper.modify",user);
sqlSession.delete("userMapper.delete",user);
//结束
sqlSession.close();
MybatisUtils
封装了Mybatis执行SQL Mapper前的流程
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
两种查询
Mybatis 查询方法
方式一 .getMapper(UserMapper.class);
需要在Mapper.xml
,使用全类名指定
<mapper namespace="com.bright.Dao.UserMapper"></mapper>
public class test {
@Test
public void testSql(){
//获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//sqlSession.getMapper() 执行SQL
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用实现类的getUserList方法
List<User> userList = userMapper.getUserList();
//遍历输出
for (User user : userList) {
System.out.println(user);
}
//关闭资源
sqlSession.close();
}
}
方式二 .selectList("namespace+id")
在mapper.xml
可以使用全类名也可以用类名,关键是要和代码中的统一
<mapper namespace="com.bright.Dao.UserMapper"></mapper>
sqlSession.selectList("com.bright.Dao.UserMapper.selectAll");
<mapper namespace="UserMapper"></mapper>
sqlSession.selectList("UserMapper.selectAll");
public class test {
@Test
public void testSql(){
//获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<User> userList = null;
//com.bright.Dao.UserMapper是namespace所指内容,selectAll是下面的方法
userList=sqlSession.selectList("UserMapper.selectAll");
//遍历输出
for (User user : userList) {
System.out.println(user);
}
//关闭资源
sqlSession.close();
}
}
配置文件解析
Mybatis_config 文件内字段分析
Properties 配置
通过配置属性来引用配置文件
db.properties
#MySQL8以下将配置改为 com.mysql.jdbc.Driver
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/cms_hisoft?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username=root
password=123456
通过properties引入外部配置文件
<properties resource="db.properties"></properties>
<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>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件同时有同名字段,优先使用外部配置文件的
类型别名
类型别名(typeAliases)设置返回类型的别名,为了解决Mapper.xml
中的使用全类名引用的情况
<mapper namespace="com.bright.Dao.UserMapper">
<select id="selectAll" resultType="com.bright.pojo.User">
select * from users
</select>
</mapper>
通过指定一个类来给起短名,如果定义了别名则可以在resultType
中直接引用
<typeAliases>
<typeAlias type="com.bright.pojo.UserAll" alias="user"/>
</typeAliases>
<mapper namespace="com.bright.Dao.UserMapper">
<select id="selectAll" resultType="user">
select * from users
</select>
</mapper>
还可以通过扫描package
来批量指定JavaBean
在没有注解的情况下会使用JavaBean
的小写名来当作别名
UserAn
-> userAn
小驼峰可以正常使用
UserAn
-> useran
全小写也可以正常使用
<typeAliases>
<package name="com.bright.pojo"/>
</typeAliases>
在使用扫描package
的情况下,可使用注解形式起别名
@Alias("user")
public class User{...}
问题
在使用别名时会导致Mybatis中DefaultVFS出现类名乱码
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ���� 4 Q
//采用*.*的方式
<select id="queryById" resultType="com.*.*.Book" parameterType="long">
......
</select>
mybatis 的 DefaultVFS 日志乱码问题 - feshfans - 博客园 (cnblogs.com)
Setting(设置)
常用来设置Log4j日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Mappers(映射器)
方式一:通过指定资源文件xml注册
<mappers>
<mapper resource="com/bright/mapper/UserMapper.xml"/>
</mappers>
泛匹配扫描
<mappers>
<mapper resource="com/bright/mapper/*Mapper.xml"/>
</mappers>
方式二:通过class指定映射
<mappers>
<mapper class="com.bright.mapper.UserMapper"/>
</mappers>
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
方式三:使用package
<mappers>
<package name="com.bright.mapper"/>
</mappers>
ResultMap
解决属性名和字段名不一致的问题,将数据表中的列与实体类中的属性对应上
UserMapper.xml
<resultMap id="" type="UserMap">
<!--column对应数据表中字段,property对应javabean中属性-->
<result column="id" property="id"/>
<result column="name" property="username"/>
<result column="pwd" property="password"/>
</resultMap>
<!--指定resultMap名字为UserMap-->
<select id="getUserList" resultMap="UserMap">
select * from user
</select>
Log4j 日志
Log4j是Apache的开源项目,Log4j可以控制日志输出的地方为控制台、GUI组件、文件等;可以设置输出日志的等级;只需要操作配置文件即可,无需改动代码。
Log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/sysrun.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
在mybatis核心文件中配置log4j
mybatis-config.xml
<configuration>
<!--引入配置-->
<properties resource="db.properties"></properties>
<!--设置日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="development">
....
</environments>
</configuration>
在类中使用
//设置log为当前类
static Logger logger = Logger.getLogger(test.class);
@Test
public void testLog4j(){
//设置log级别
logger.info("info:进入了Log4j");
logger.debug("debug:进入了Log4j");
logger.error("error:进入了Log4j");
}
注解实现CRUD
之前提到的都是用Mapper映射xml实现的sql语句操作数据库,sql语句主要是写到了xml文件中,Mybatis还提供了注解实现sql语句操作数据库,更加简化了代码编写。但是如果是更复杂的sql,还是需要用xml来编写,注解的方式只适合一些简单的sql语句。
userMapper
接口
//查询所有用户
@Select("select * from user")
List<User> getUserList();
//根据id查询用户,利用@param注解传参
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
//add user
@Insert("insert into user (id, name, pwd) values (#{id}, #{username}, #{password})")
int addUser(Map map);
//update user
@Update("update user set name=#{username},pwd=#{password} where id = #{id}")
int updateUser(User user);
//delete user
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
关于@Param注解
- 用于基本类型或String,引用类型不需要
- 在sql中引用的就是这里@Param中的值
mybatis-config.xml
绑定接口(注意用注解绑定的就是接口)
<mappers>
<mapper class="com.bright.mapper.UserMapper"></mapper>
</mappers>
测试
static Logger logger = Logger.getLogger(test.class);
@Test
public void testGetUserList(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
logger.info("info:"+user);
}
sqlSession.close();
}
关于#{}与${}
#{}为预编译;${}为直接拼接,可能引发sql注入
Mybatis-动态SQL
动态SQL:利用Mybatis中自带的标签,根据不同的条件生成不同的SQL语句。
if
<select id="selectById" parameterType="Map" resultMap="Student">
select * from user where
<if test="id != null!">
id = #{id}
</if>
</select>
where
<select id="select" parameterType="Map" resultMap="Student">
select * from user where
<if test="id != null!">
id = #{id}
</if>
<if test="id != null!">
and name = #{name}
</if>
</select>
上面的SQL语句在拼接的过程中,可能会出现以下情况造成SQL语句执行错误
select * from user where and name = xxx;
可以使用where标签来解决这个问题,where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除,将代码改为
<select id="select" parameterType="Map" resultMap="Student">
select * from user
<where>
<if test="id != null!">
id = #{id}
</if>
<if test="name != null!">
and name = #{name}
</if>
</where>
</select>
set
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
set 元素可以和if
或choose
用于动态包含需要更新的列,忽略其它不更新的列;
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
choose
类似于java中swtich case语句,满足when标签中的表达式,则执行该when中的sql语句,若都不满足则执行otherwise中的sql语句
<select id="StudentMapper" parameterType="Map" resultMap="Student">
select * from user
<where>
<choose>
<when test="id != null!">
id = #{id}
</when>
<when test="name != null!">
and name = #{name}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
trim
mybatis的trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
//拼接的前缀
prefix=""
//去除指定前缀
prefixOverrides=""
//拼接后缀
suffix=""
//去除指定后缀
suffixOverrides=""
场景模拟:去除多余and
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
以上动态SQL运行后,可能会出现以下两个问题
问题一SQL
SELECT * FROM BLOG
WHERE
问题二SQL
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
解决方法
使用where解决
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
使用trim解决
<trim prefix="WHERE" prefixOverrides="AND">
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</trim>
场景模拟:update去除多余 ,
<insert id="insert" parameterType="com.bright.user">
insert into user
(
<if test="name!=null" >
name,
</if>
<if test="gender!=null" >
gender
</if>
)
values(
<if test="name!=null" >
name,
</if>
<if test="gender!=null" >
gender
</if>
)
</insert>
问题SQL
Insert into user(name,) values(name,);
解决方法
<insert id="insert" parameterType="com.bright.user">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name!=null" >
name,
</if>
<if test="gender!=null" >
gender
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="name!=null" >
name,
</if>
<if test="gender!=null" >
gender
</if>
</trim>
</insert>
参考
(4条消息) Mybatis快速入门_kopoo的博客-CSDN博客_mybatis坐标