MyBatis
一、Mybatis简介
1.1 什么是Mybatis
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- Mybatis可以简化数据库编程,底层封装的是JDBC,在使用Mybatis实现数据库编程时,只需要定义每个数据操作功能的抽象方法,并配置这个抽象方法对应的SQL语句即可。
1.2 导入SSM组件
- MyBatis经常和Spring、SpringMVC一起使用,简称为SSM框架。
- 导入SSM的依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
- 配置MyBatis数据库连接参数
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.username=root
db.password=root010216
db.maxActive=10
db.initialSize=2
- 创建Spring配置类,配置数据库连接池
package com.yuanle.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:jdbc.properties")
public class MybatisConfig {
@Bean
public DataSource dataSource(
@Value("${db.driver}") String driver,
@Value("${db.url}") String url,
@Value("${db.username}") String username,
@Value("${db.password}") String password,
@Value("${db.maxActive}") int maxActive,
@Value("${db.initialSize}") int initialSize){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setMaxActive(maxActive);
ds.setInitialSize(initialSize);
return ds;
}
}
1.3 Mybatis helloWorld
- 在Mybatis的配置类中配置Mtbatis的核心工厂SqlSessionFactory,需要将dataSource注入给它,这样Mybatis才能正确连接到数据库。
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
-
在Mybatis的配置类上标注@MapperScan(basePackages=“com.yuanle.mapper”),目的是自动查找mapper接口,并且自动创建接口的实现类。
-
声明数据访问接口DemoMapper,MyBatis会自动创建这个接口的具体实例。
package com.yuanle.mapper;
import org.apache.ibatis.annotations.Select;
public interface DemoMapper {
@Select("SELECT 'HelloWorld!'")
String hello();
}
- 运行原理是:在DemoMapper接口中抽象方法前面标注了@Select注解,这个注解为hello方法声明了SQL语句,MyBatis会自动处理这个注解:在调用hello方法时候,MyBatis会自动向数据库发送注解中的SQL语句,接着数据库执行SQL返回数据,MyBatis还会自动将SQL返回结果作为hello方法的返回值。
二、利用MyBatis实现数据表CRUD操作
2.1 初始化数据库
create database test;
use test;
create table user(
id int(11) auto_increment comment '用户id',
username varchar(20) unique not null comment '用户名',
password varchar(20) not null comment '密码',
age int comment '年龄',
phone varchar(20) comment '电话',
email varchar(20) comment '电子邮箱',
primary key(id)
) ENGINE=INNODB default charset=utf8;
INSERT INTO user(username,password,phone,age,email) VALUES
('Frank01','1234','13800138001',28,'frank01@163.com'),
('Frank04','1234','13800138004',33,'frank04@163.com'),
('Frank05','1234','13800138005',21,'frank05@163.com'),
('Frank09','1234','13800138009',26,'frank09@163.com'),
('Frank06','1234','13800138006',29,'frank06@163.com'),
('Frank11','1234','13800138011',24,'frank11@163.com'),
('Frank12','1234','13800138012',23,'frank12@163.com'),
('Frank07','1234','13800138007',31,'frank07@163.com'),
('Frank08','1234','13800138008',22,'frank08@163.com'),
('Frank02','1234','13800138002',27,'frank02@163.com'),
('Frank03','1234','13800138003',32,'frank03@163.com'),
('Frank10','1234','13800138010',30,'frank10@163.com'),
('Frank13','1234','13800138013',25,'frank13@163.com');
2.2 查询功能
-
MyBatis会自动的将数据库的信息加载到JavaBean对象中,创建User类,和数据库中表对应。
-
声明数据库访问接口UserMapper,在接口中声明数据访问方法,这个借口并不需要实现,MyBatis最强大的功能就是自动实现这个接口,并且实现相应的功能。在UserMapper中设计一个根据用户的ID查询一个用户信息的方法findUserById,参数就是用户的主键ID
@Select("SELECT id, username, password, age, phone, email FROM user WHERE id=#{id}")
User findUserById(Integer id);
- 然后测试,原理就是MyBatis利用JDBC查询数据库,并将查询结果映射到User对象属性,然后将User对象作为findUserById方法结果返回。
- 上述是查询单条数据,查询多条数据也不难,就是将数据访问方法的返回值改为List,然后遍历List,即可获得。
@Select("SELECT id, username, password, age, phone, email FROM user")
List<User> fingAll();
@Test
public void testFindAll(){
UserMapper userMapper = ctx.getBean("userMapper", UserMapper.class);
List<User> users = userMapper.fingAll();
for(User user: users){
System.out.println(user);
}
}
2.3 新增功能
-
只需要将SQL语句需要插入数据的地方替换为#{}占位,并且使#{}中的名字与User对象的属性名一致,然后在数据访问接口中声明抽象方法,使用@Insert映射SQL语句于方法的对应关系。MyBatis执行时候就会自动读取参数的属性填充到对应占位符#{}。
-
注意:这样插入之后我们的User类中的自增id是获取不到的,需要在抽象方法上加@Options注解,其中属性添加useGeneratedKeys = true, keyProperty = “id”。
@Insert("INSERT INTO user (username, password, age, phone, email) " +
"VALUES (#{username}, #{password}, #{age}, #{phone}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
Integer insertUser(User user);
2.4 修改功能
- 在接口方法上使用@Update声明更新语句,就可以完成更新功能。但是这种方法会更新所有的值。
- 若只想修改其中的几个值,那么SQL语句中就会有多个参数,所以,在设计抽象方法的时候,抽象方法的参数列表中也应该有多个参数,这么做调用方法的时候,传过去的参数MyBatis在处理时参数名称其实不是我们穿过去的值,而是arg0、arg1…,它就会找不到映射参数,这个时候我们可以用@Param()注解填上我们要改的参数值,就可以了。
@Update("UPDATE user SET password=#{password} WHERE id=#{id}")
Integer updatePasswordById(@Param("password") String password, @Param("id") Integer id);
2.5 删除功能
- 删除功能和上述功能实现方式完全一致。
三、动态SQL拼接
3.1 XML文件映射
-
MyBatis提供了注解定义SQL与Mapper接口的映射关系,还提供了利用XML文件映射与接口方法的关系。相对于注解,xml文件更加灵活强大,可以实现复杂的动态SQL拼接,以及复杂结果映射功能。
-
使用XML文件映射步骤:
(1)首先在数据访问接口中定义抽象方法
(2)创建XML文件为接口SQL提供映射关系,XML文件中的标签及属性功能介绍:
a. 标签中用于书写各种SQL语句,其中的namespace属性指向数据访问接口的全类名
b. 标签中的id属性指向数据访问接口中的抽象方法的方法名,resultType属性用于声明数据访问接口中的抽象方法的返回值。
(3)在sqlSesstionFactroy配置中加载XML文件,并将映射文件的位置配置在properties文件中
@Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource, @Value("${mybatis.mapper.location}") Resource[] mapperLocations) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(mapperLocations); return bean.getObject(); }
mybatis.mapper.location=classpath:mappers/*.xml
3.2 动态SQL-foreach
-
动态SQL:是指根据调用方法时给出的参数不同,最终生成的SQL语句会不相同
-
foreach节点标签可以实现动态SQL中的遍历,遍历参数值来生成最终的SQL。
(1)使用如下:
public interface UserMapper {
Integer deleteByIds(Integer... ids);
}
<mapper namespace="com.yuanle.mapper.UserMapper">
<delete id="deleteByIds" parameterType="int">
DELETE FROM user WHERE id IN(
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</delete>
</mapper>
(2)上述配置中各属性的作用
a. connection:表示被遍历的对象,当抽象方法的参数只有1个且没有添加@Param注解时,如果参数的类型是List集合类型的,则取值为list,如果参数的类型是数组类型的,则取值为array;否则,取值是@Param注解中配置的名称。
b. item:在遍历过程中,被遍历到的数据的名称,将根据这个名称来使用被遍历到的数据,所以,在的子级,将根据这个属性配置的值来使用数据,
c. separator:分隔符,用于分隔遍历时生成的多个占位符。
d. open与close:遍历生成SQL语句片段的多个占位符。
3.3 显示MyBatis生成的SQL
- MyBatis提供了显示动态生成SQL功能,利用这个功能可以查看SQL,分析问题原因,甚至是优化查询性能。这个功能是利用日志系统实现的,需要先导入日志包。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
- 导入包之后还应该添加日志配置文件log4j.properties
log4j.rootLogger=ERROR, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
# MyBatis Mapper:控制日志输出
log4j.logger.com.yuanle.mapper.UserMapper=TRACE
3.4 动态SQL-if
- 标签的配置语法格式是:
<if test="表达式">
SQL语句片段
</if>
- 使用可以根据判断条件不同而得到不同的SQL语句,在使用时,如果有多个条件存在逻辑关系,可以使用“and”表示“与”,使用“or”表示“或”,在MyBatis中的并没有对应的else标签,如果要实现if…else的效果,需要使用系列的标签,基本格式是:
<choose>
<when test="表达式">
满足条件时执行的SQL语句片段
</when>
<otherwise>
不满足条件时SQL语句片段
</otherwise>
</choose>
-
使用有条件的SQL时,可能会发生多“,”的BUG,MyBatis考虑到这个情况提供了标签消除多余的符号。
-
在进行多参数动态查询时,where条件也要使用标签,这个标签可以为我们去除多余的AND或添加符号等等。
<select id="findUserByParams" resultType="com.yuanle.entity.User">
SELECT
id, username, password, age, phone, email
FROM
user
<where>
<if test="username != null">
username like #{username}
</if>
<if test="age !=null">
AND age = #{age}
</if>
<if test="phone != null">
AND phone like #{phone}
</if>
</where>
</select>
3.5 xml文件中其它标签的意义及属性
-
resultMap:用于映射复杂的SQL语句。
-
result:用于将列名和属性名映射一致
(1)column属性:用于指定查询结果中的列名
(2)property属性:用于对应javaBean中的属性
-
collection:用于遍历JavaBean中的集合,其中property属性对应JavaBean中集合的名字,ofType用于指定集合中泛型的类型。
3.6 关于#{}和${}格式的占位符
- 在MyBatis中,配置SQL语句时,可以使用#{}或${}格式的占位符,用于表示参数的值。
- 使用#{}格式的占位符时,只能表示某个参数值,不可以表示SQL语句中的某个片段,使用这种占位符时,MyBatis在处理过程中,是使用预编译的做法,所以,这种做法是安全的,不存在SQL注入的风险。
- 使用${}格式的占位符,可以表示SQL语句中的任何部分,可以是某个值,也可以是SQL语句中的某个片段。使用这种占位符时,MyBatis在处理过程中,是先将占位符对应的值拼接到SQL语句中,这种没有预编译的处理,存在SQL注入风险,使用时要甚用。
@Delete("DELETE FROM ${table} WHERE id=#{id}")
Integer deleteById(
@Param("table") String table,
@Param("id") Integer id
);