根据MyBatis :对象关系映射(ORM,Object Relational Mapping)框架,用于增强jdbc,访问数据库,执行增删改查。
MyBatis 使用步骤:
- 在pom.xml中导入Maven依赖:
- 数据库驱动
- MyBatis依赖
- Druid连接池
- MyBatis主配置文件mybatis.xml:
- 数据库连接
- 指定mapper文件的位置
- Dao接口和Mapper文件
- Dao接口:定义了操作数据的方法
- Mapper文件:sql映射文件,和Dao接口对应的sql语句
- 创建MyBatis的对象SqlSession,通过它的方法执行Sql语句
基本使用
1、在pom.xml中导入Maven依赖:
<?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>
<groupId>org.example</groupId>
<artifactId>demo-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<encoding>utf-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!-- 资源拷贝插件 -->
<resources>
<resource>
<!-- 指定所在目录 -->
<directory>src/main/java</directory>
<includes>
<!-- 设置要导入的资源文件 -->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
资源拷贝插件:
- 如果将 mapper 文件放在 resources 文件夹下,则不需要配置资源拷贝
- 在将 mapper 文件和 dao 接口中的方法放在一起,则需要配置该插件
2、MyBatis配置文件 resources/mybatis.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>
<!-- 配置导入 -->
<properties resource="db.properties"/>
<!-- 数据库信息的配置 -->
<environments default="development">
<environment id="development">
<!-- transactionManager:mybatis的事务类型
type:jdbc(表示使用jdbc中的Connection对象的commit、rollback做事务处理)
-->
<transactionManager type="JDBC"/>
<!-- 数据源类型 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 指定sql mapper文件位置 -->
<mappers>
<!-- 从类路径开始导入 -->
<mapper resource="top/songfang/dao/UserMapper.xml"/>
</mappers>
</configuration>
resources/db.properties:
jdbc.username=root
jdbc.password=1234
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
3、创建Dao接口:定义了操作数据的方法
public interface UserMapper {
// 查询所有数据
List<User> selectAll();
// 插入数据
int insert(User user);
}
4、创建对应的UserMapper.xml文件
- dao 接口和 mapper.xml 文件放在同一个文件夹
- dao 接口和 mapper.xml 文件名称一致
- mapper.xml 文件中的 namespace 的值 dao 接口的全限定类名称
- dao 接口中不要使用重载方法,不要使用同名、不同参数的方法
- mapper.xml 文件中的
select id,name,age,male from t_user
<insert id="insert" parameterType="top.songfang.domain.User">
insert into t_user(id,name,age,male)
values(#{id},#{name},#{age},#{male})
</insert>
5、测试:
```java
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
// 1、MyBatis配置文件路径
String config = "mybatis.xml";
try {
// 2、从配置文件获取输入流
InputStream inputStream = Resources.getResourceAsStream(config);
// 3、创建SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 4、获取SQLSession对象
public static SqlSession openSession(){
if (sqlSessionFactory != null) {
return sqlSessionFactory.openSession();
}
return null;
}
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.openSession();
assert sqlSession != null;
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
users.forEach(System.out::println);
}
}
重要类说明:
- org.apache.ibatis.io.Resources:MyBatis的工具类,用于读取配置文件获取输入流
- SqlSessionFactoryBuilder:用于创建SqlSessionFactory的工厂类
- SqlSessionFactory:重量级资源,是一个接口
- 其默认实现类 DefaultSqlSessionFactory
- 主要用于 SqlSession:SqlSession sqlSession=factory.openSession();
- openSession():无参数,获取的时非自动提交事务的SqlSession对象
- openSession(boolean):true,获取自动提交事务的SqlSession对象;false,非自动提交事务的SqlSession对象
- SqlSession:接口
- 其默认实现类:DefaultSqlSession
- 定义了操作数据的方法,例如selectOne()、selectList()、insert()、update()、delete()、commit()、rollback()等
- SqlSession不是线程安全的,需要在方法内部使用,在执行sql语句之前使用openSession()获取SqlSession,在执行完毕后应及时关闭它sqlSession.close()
参数传递
参数传递是指从 java 代码中把实际的值传到 mapper 文件中,参数类型可以使用 parameterType 进行指定
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型:
BIT
FLOAT
CHAR
TIMESTAMP
OTHER
UNDEFINED
TINYINT
REAL
VARCHAR
BINARY
BLOB
NVARCHAR
SMALLINT
DOUBLE
LONGVARCHAR
VARBINARY
CLOB
NCHAR
INTEGER
NUMERIC
DATE
LONGVARBINARY
BOOLEAN
NCLOB
BIGINT
DECIMAL
TIME
NULL
CURSOR
ARRAY
1、单个参数:#{任意字符}
UserMapper接口:public interface UserMapper {
// 通过传入的id查询User
User selectById(int id);
}
UserMapper.xml:
- 只有一个参数时可以在select标签中通过parameterType指定传入的参数的类型,参数类型支持类的全限定名(建议)和别名,但是一般可以省略不写
- 只有一个参数时通过 #{任意字符} 来获取传入参数的值,一般为了可读性,与Mapper接口中方法的参数名保持一致
<select id="selectById" parameterType="java.lang.Integer" resultType="top.songfang.domain.User">
select id,name,age,male from t_user
where id = #{id}
</select>
2、多个参数:支持三种方式
- 在Mapper.xml文件中根据在方法中的位置进行获取,有两种获取规则
- 使用 @Param 注解在Mapper接口的方法的参数前自定义名称,然后在Mapper.xml文件中通过 #{名称} 获取【建议使用】
UserMapper接口:
// 不使用@Param指定参数名称,使用参数位置进行获取
public interface UserMapper {
// 通过id或name进行查找
List<User> selectByIdAndName(Integer id,String name);
}
// 使用@Param自定义参数名称
public interface UserMapper {
// 通过id或name进行查找
List<User> selectByIdAndName(@Param("id") Integer id, @Param("name") String name);
}
UserMapper.xml:
- 参数类型指定:支持在 #{paramName , javaType= , jdbcType= } 中通过 javaType(全限定类名) 和 jdbcType 来分别指定传入参数的 java 类型和 jdbc 类型,如 #{name , javaType=java.lang.String , jdbcType=VARCHAR}
- 由于 MyBatis 能够通过反射自动推断获取参数的类型,一般在使用时不用指定类型 ```xml
3、多个参数:使用对象进行传参
- 将对象作为参数传入,使用对象中的属性来传递具体的参数值,语法 #{属性名 , javaType=类型名称 , jdbcType=数据类型}
- javaType:java中属性数据类型,使用全限定名
- jdbcType:在数据库中的数据类型,在mybatis中定义了一个jdbcType的枚举类中
- JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)
- 支持自定义类型处理:#{age , javaType=int , jdbcType=NUMERIC , typeHandler=MyTypeHandler},实现 TypeHandler 接口
- 在update标签中使用parameterType="类的全限定名"指定传入对象的类型
UserMapper接口:
```java
public interface UserMapper {
// 通过id更新用户数据,返回值为本次事务影响的数据库行数
int update(User user);
}
UserMapper.xml 文件:
<update id="update" parameterType="top.songfang.domain.User">
update t_user
set name=#{name},age=#{age},male=#{male}
where id=#{id}
</update>
4、多个参数,使用 Map 进行传参
- 类似使用对象传参的方式,使用 #{map中的key名} 来传参
UserMapper接口:
public interface UserMapper {
// 通过Map进行传参
int insertUser(Map<String,Object> map);
}
UserMapper.xml:
<insert id="insertUser">
insert into t_user(id,name,age,male)
values (#{id},#{name},#{age},#{male})
</insert>
测试:
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
// 1、MyBatis配置文件路径
String config = "mybatis.xml";
try {
// 2、从配置文件获取输入流
InputStream inputStream = Resources.getResourceAsStream(config);
// 3、创建SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 4、获取SQLSession对象
public static SqlSession openSession(){
if (sqlSessionFactory != null) {
return sqlSessionFactory.openSession();
}
return null;
}
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.openSession();
assert sqlSession != null;
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser(new HashMap<String, Object>() {{
put("id",5);
put("name", "王五");
put("age", 18);
put("male", false);
}});
sqlSession.commit();
sqlSession.close();
}
}
$ 和 # 的区别:
{} 占位符,告诉 MyBatis 使用实际的参数值代替,并使用 PreparedStatement 对象执行 sql 语句,#{…} 代替 sql 语句的 ?【首选】
- 不会有 sql 注入的风险,安全性较高
- 进行了预处理,效率较高
- ${} 占位符,只会做字符串的替换,不会修改或者转义该字符串,有 sql 注入的风险,安全性低,效率低
- ${} 占位符在一般用于元数据(如表名、列名、不同列排序等)动态生成时十分有用
- 比如:order by ${columnName},MyBatis 就不会修改或转义该字符串
UserMapper接口:
public interface UserMapper {
// 可以自定义查询的列和查询值
User findByColumn(@Param("column") String column, @Param("value") String value);
}
UserMapper.xml:
- ${column} 会直接被替换
{value} 会使用 ? 预处理
<select id="findByColumn" resultType="top.songfang.domain.User">
select id,name,age,male from t_user
where ${column} = #{value}
</select>
like 子句:有多种写法
public interface UserMapper {
// 可以自定义查询的列和查询值
List<User> find(@Param("pattern") String pattern);
}
1、用 java 代码指定 like 的内容
<select id="find" resultType="top.songfang.domain.User">
select id,name,age,male from t_user
where name like #{pattern}
</select>
测试:
List<User> users = userMapper.find("%王%");
users.forEach(System.out::println);
2、在 mapper 中拼接 like 的内容
{} 在解析成 sql 语句的时候,会在变量外侧自动加单引号,所以%需要使用双引号
测试:<select id="find" resultType="top.songfang.domain.User">
select id,name,age,male from t_user
where name like "%" #{pattern} "%"
</select>
3、使用 contact 内部函数List<User> users = userMapper.find("王");
users.forEach(System.out::println);
4、使用 mybatis 的 bind 标签<select id="find" resultType="top.songfang.domain.User">
select id,name,age,male from t_user
where name like CONCAT("%",#{pattern},"%")
</select>
bind 标签可以使用 OGNL 表达式以外创建一个变量并将其绑定到上下文中
<select id="find" resultType="top.songfang.domain.User">
<bind name="pattern" value=" '%' + pattern + '%' "/>
select id,name,age,male from t_user
where name like #{pattern}
</select>
结果映射
MyBatis 执行了 sql 语句,得到了 java 对象:resultMap和resultType二选一
处理方式:
- mybatis 执行 sql 语句,然后 mybatis 调用类的无参数构造方法,创建对象
- mybatis 把 ResultSet 指定列值付给同名的属性
ResultSet rs = executeQuery("select id,name,age,male from t_user");
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setMale(rs.getBoolean("male"));
}
- resultType:sql 执行完毕后,数据转换为 javaBean 或 POJO 领域模型 ,java 类型是任意的
- resultType:类的全限定名
- resultType:类型的别名,如 java.lang.Integer 别名是 int
- resultMap:可以自定义 sql 的结果和 java 对象属性的映射关系,更灵活的把列值赋值给指定属性,常用在列名和java对象属性名不一致的情况
- 先定义 resultMap ,指定列名和属性的对应关系
- 在