Mybatis
Mybatis概述
Mybatis是什么
Mybatis是一个持久层框架。
Mybatis的作用
Mybatis是一个持久层框架,当然作用就是操作数据库的(增删改查).
为什么需要学习Mybatis
Mybatis的理念:让开发者是10%的代码就可以实现数据库的操作。
解决方案: 持久层(DAO)的零实现。所谓的零实现,就是不用写实现类代码,直接使用接口就可以操作数据库。
Mybatis的定义,提高开发的效率!!!真的写很少代码!!!就可以增删改查。
Mybatis的作用
Mybatis框架能够让我们以最少的代码就可以操作数据库。从而提高开发的效率!!!
如何将代码减少呢?
持久层的零实现 (不需要写实现类):可以自动将数据封装到对象里面不需要手工编写映射的关系 。
Mybatis配置流程图
- Resources:资源类,用于读取总配置文件
- SqlSessionFactoryBuilder:会话工厂构造类,通过读取的总配置文件构建会话工厂
- SqlSessionFactory:会话工厂
- SqlSession:会话,就是操作数据库的操作类
入门示例
配置流程说明
- 导入包(任何框架需要的事情)
- 创建总配置文件,文件名随便写
- 创建一个MybatisUtils工具类,(获得操作对象)
- 创建一个映射接口
- 创建一个映射文件
- 在总配置文件加载映射文件。
- 编写测试插入数据代码
配置步骤
下载mybatis框架
https://github.com/mybatis/mybatis-3
创建一个普通java项目并导入相关
创建主配置文件 mybatis-config.xml
在项目的 src 下面创建 配置文件 mybatis-config.xml,具体配置参考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">
<!-- mybatis 配置标签,内部就是mybatis的具体配置 -->
<configuration>
<!-- mybatis的环境配置
default :默认使用环境,值就是下面环境的id
-->
<environments default="mysql">
<!-- mybatis的具体某一个环境 -->
<environment id="mysql">
<!-- MyBatis操作需要事务管理,默认使用 JDBC
JDBC : MyBatis的别名
-->
<transactionManager type="JDBC"/>
<!-- 配置MyBatis的数据源 (连接池)
POOLED(别名) : MyBatis内置的一个连接池
后期和Spring集成使用 ,Druid 阿里巴巴连接池
-->
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- url地址 -->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<!-- 账号 -->
<property name="username" value="root"/>
<!-- 密码 -->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件 -->
<mappers>
<!-- 读取单个映射文件 -->
<mapper resource="cn/zj/mybatis/mapper/UserMapper.xml"/>
</mappers>
</configuration>
创建MyBatisUtil工具类
MyBatisUtil工具类的作用主要用于 读取配置文件,创建工厂对象,提供创建SqlSession数据库操作对象的方法
package cn.zj.mybatis.util;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil {
private MyBatisUtil() {
}
//SqlSessionFactory 会话工厂对象
private static SqlSessionFactory factory;
//类加载到JVM中就立马执行static代码块,并且只会执行一次
static {
//资源文件
String resource = "mybatis-config.xml";
//try(){}catch(Exception e){} try的圆括号内部能够自动释放/关闭资源
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
//创建SqlSessionFactory 对象
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建SqlSession对象
* @return SqlSession对
*/
public static SqlSession openSession() {
//创建Session对象
SqlSession session = factory.openSession();
return session;
}
}
创建数据库表对应的实体类
即 pojo,略
创建一个操作接口
类似于 DAO 接口,命名方式为后缀加 Mapper,例如 UserMapper.java
创建表对应的映射文件 :UserMapper.xml
映射文件的命名与接口名对应,一起放在 mapper 包下。
<?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">
<!-- 映射标签,内部就编写具体的映射sql语句
namespace :命名空间,值 必须是 对应接口的全限定名 = 包名 +简单类名
cn.zj.mybatis.mapper.UserMapper
-->
<mapper namespace="cn.zj.mybatis.mapper.UserMapper">
<!-- <insert id="insertUserInfo" parameterType="">
插入功能标签
id : 功能唯一标识, 必须接口中对应的方法名一直
parameterType : 参数类型,必须和接口中方法的参数类型一直
keyColumn="" : 结果集对应的主键列
keyProperty="" pojo对象对应结果集主键列的属性
useGeneratedKeys="true" :是否返回自动生成的主键 true
这样设置后,数据库自动生成的主键(如id)会自动添加到用于插入的对象上,便于后续对该对象的操作
-->
<insert id="insertUserInfo" parameterType="cn.zj.mybatis.pojo.User"
keyColumn="id"
keyProperty="id"
useGeneratedKeys="true"
>
<!--
#{对象属性} : OGNL 表达式语言
-->
insert into user (name,password,age)values(#{name},#{password},#{age})
</insert>
<!-- 单行查询
<select resultType ="">
查询功能的标签
resultType : 返回结果的数据类型,和接口对应方法的返回类型必须一致
-->
<select id="selectByPrimaryKey" parameterType="Integer" resultType="cn.zj.mybatis.pojo.User">
<!-- #{对象属性} ,主键理论上任何值都可以 #{abc},#{xxx},可以不用对象属性-->
select * from user where id = #{id}
</select>
<!-- 多行查询
无论单行或者多行查询,返回的数据类型都是 对应 pojo 对应的对象类型
-->
<select id="selectList" resultType="cn.zj.mybatis.pojo.User">
select * from user
</select>
<!-- 删除操作 -->
<delete id="deleteByPrimaryKey" parameterType="Integer">
delete from user where id = #{id}
</delete>
<!-- 修改操作 -->
<update id="updateUserInfo" parameterType="cn.zj.mybatis.pojo.User">
update user set name = #{name},password = #{password},age = #{age} where id = #{id}
</update>
</mapper>
3 log4j日志框架的配置
3.1 说明
log4j是一个日志输出框架,就是用于输出日志的。
Mybatis的日志输出是通过Log4J输出的。主流框架大部分都是Log4j输出的。Spring框架也可以通过Log4j输出日志!!
问题:既然Log4j功能类似System.out.println(),为什么使用log4j而不直接使用System.out.println()?
答:Log4j提供了强大的日志输出的自定义功能。
- 通过级别输出日志 (调试、信息、警告、错误、致命异常)
- 可以指定输出到控制台,以及输出到文件。
- 可以设置输出的日志格式
所以学习LOG4J.需要学会自定义配置LOG4J的输出格式以及输出等级
3.2 下载路径
Log4j的下载地址:http://logging.apache.org/log4j/1.2/
3.3 配置步骤
3.3.1 第一步:导入log4j-1.2.17.jar的包
3.3.2 第二步:在src下创建一个log4j.propertis文件
注意:文件名必须为log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
# log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 前缀(log4j.logger)+点(.)需要记录日志的命名空间 = 日志级别
log4j.logger.cn.zj.mybatis.mapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
4 MyBatis完成CRUD操作
5 ResultMap 手动映射
MyBatis的查询结果集都是自动映射封装的,单行查询将数据库一条数据封装成对应的Java对象。多行查询,先将每一行封装成对象,再将每个对象添加到集合中,最后返回一个List集合对象。
但是:必须保证查询结果集和pojo对象的属性名相同,否则无法自动封装
问题: 如何解决查询结果集名称和pojo对象属性不同的映射封装?
解决方案:
- 使用手动映射封装
<ResultMap>
标签 - 可以使用mybatis的驼峰命名法
<?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 : 命名空间,通俗讲,每个映射文件唯一的标识名称
-->
<mapper namespace="cn.zj.mybatis.mapper.UserMapper">
<!--
resultType : 自动映射
resultMap : 手动映射,值 自定义对应手动映射标签的 id的值
注意:自动映射和手动映射二选一,不能同时使用
-->
<select id="findByUserId" parameterType="Integer" resultMap="user_map">
<!-- #{} OGNL表达式语言 -->
select id u_id,name u_name,password u_password ,age u_age from user where id = #{id}
</select>
<!-- 手动映射
type :需要手动映射的数据类型
id :唯一标识
-->
<resultMap type="User" id="user_map">
<!--
主键列映射
<id column="" property="" javaType="" jdbcType=""/>
column :结果的列名
property:domain对象的对应的属性名
javaType :domian对象的属性的类型(可选,默认自动关联)
jdbcType :结果集列类型(可选,默认自动关联)
-->
<id column="u_id" property="id" javaType="Integer" jdbcType="INTEGER"/>
<!--
主键列映射
<result column="" property="" javaType="" jdbcType=""/>
column :结果的列名
property:domain对象的对应的属性名
javaType :domian对象的属性的类型(可选,默认自动关联)
jdbcType :结果集列类型(可选,默认自动关联)
-->
<result column="u_name" property="name"/>
<result column="u_age" property="age"/>
<result column="u_password" property="password"/>
</resultMap>
<!-- 多行查询
注意:查询不管单行还是多行查询,返回的数据类型都是数据表对应的domain类型
多行不是返回集合
-->
<select id="selectAll" resultMap="user_map">
select id u_id,name u_name,password u_password ,age u_age from user
</select>
</mapper>
6 主配置文件说明与细节配置
总配置文件的标签顺序
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- objectWrapperFactory
- reflectorFactory
- plugins
- environments
- databaseIdProvider
- mappers
6.1 别名typeAliases标签
在UserMapper.xml文件中User无论是作为参数还是作为查询返回数据类型,都需要写上全限定名,实际可以写上简单类名即可,但是需要配置别名
MyBatis框架提供了两种别名机制,一种是自定义别名,一种是内置别名
6.1.1 自定义别名
<!-- 配置类型别名 -->
<typeAliases>
<!-- 使用包扫描配置别名
<package name="包名">
被配置的包下面的所有的类都被取了别名,适用包下面有大量类
别名就是当前包下面类的简单类名,默认不区分大小
-->
<package name="cn.zj.mybatis.pojo"/>
<!-- 配置单个类的别名
<typeAlias type="" alias=""/>
type:要设置别名的数据类型
alias :别名
-->
<!-- <typeAlias type="cn.zj.mybatis.pojo.User" alias="User"/> -->
</typeAliases>
如果配置成功,在映射文件里面可以直接使用别名
<select id="findById" parameterType="Integer" resultType="User">
<!-- #{} OGNL表达式语言 -->
select * from user where id = #{id}
</select>
6.1.2 内置别名
所谓的内置别名,就是Mybatis框架自带别名。Mybatis已经将常用的数据类型的别名内置声明了,所以这些内置的别名不需要配置就可以直接使用。(不区分大小写)
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | java.lang.String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | java.util.Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | java.util.Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
6.2 properties 读取配置文件
一般开发会将单独的数据库连接字符串配置到一个独立的 以 .properties 的配置文件中
Mybaits框架中配置文件 的 <properties>
标签可以读取配置文件中的内容。并可以使用 ${} 的语法设置给框架的数据库连接操作代码
6.2.1 在classpath下面创建一个db.properties数据库连接配置文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
6.2.2 在mybatis-config.xml主配置文件中配置<properties>
标签读取配置文件
<properties resource="db.properties"/>
6.2.3 连接数据库的配置修改为 ${key}的方式
<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>
6.3 settings标签
Mybatis默认设置了很多默认配置.有时候,我们需求与默认的配置的参数不一样,我们就需要修改这些默认配置的参数.
如:Mybatis已经对骆驼命名法的支持,但默认是不开启的.可以通过 mapUnderscoreToCamelCase 参数设置为 true 支持
7 MyBatis的注解开发
MyBatis的映射配置除了使用xml配置以外,还支持注解配置sql语句
问题: 为什么有了xml配置还有注解配置
答 :MyBatis的注解开发更简洁,只需要将对应的SQL语句的注解标注对应的功能方法上即可,直接连 XxxMapper.xml映射文件都可以省略了
本身注解开发就是Java配置的一种趋势,后期学习SpringBoot时候,发现全部用纯注解配置
MyBatis提供了下面注解进行映射文件配置
@Select 查询数据注解
@Insert 插入数据注解
@Delete 删除数据注解
@Update 修改数据注解
@Options 选项配置
@Results 手动映射配置
@Result @results中的具体的某一列的映射信息配置
7.1 案例代码
package cn.zj.mybatis.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import cn.zj.mybatis.domain.User;
public interface UserMapper {
/**
* 插入用户数据
* @param user 封装有用户信息的User对象
* @return 受影响的行数
*/
@Insert("insert into user (name,password,age)values(#{name},#{password},#{age})")
/*
* @Options
* 注解可选项配置
* keyColumn :结果集对应的主键列
* keyProperty pojo对象对应主键列属性
* useGeneratedKeys 是否返回自动生成的主键 true是
*/
@Options(keyColumn="id",keyProperty="id",useGeneratedKeys=true)
int insertUserInfo(User user);
/**
* 单行查询
* @param id 主键id
* @return 查询结果封装的User对象
*/
@Select("select id u_id,name u_name,password u_password,age u_age from user where id = #{id}")
/*
* 结果集封装映射
* @Results
* @Rsutlt 具体某一列属性的映射
* id :是否是主键列
* column :结果集的列明
* property :pojo对象对应的属性名
*/
@Results({
@Result(id=true,column="u_id",property="id"),
@Result(column="u_name",property="name"),
@Result(column="u_password",property="password"),
@Result(column="u_age",property="age")
})
User selectByPrimaryKey(int id);
/**
* 多行查询
* @return 结果集封装的对象
*/
@Select("select * from user")
List<User> selectList();
/**
* 删除操作
* @param id 主键id
* @return 受影响的行数
*/
@Delete("delete from user where id = #{id}")
int deleteByPrimaryKey(int id);
/**
* 修改操作
* @param user 带有id的user对象
* @return 受影响的行数
*/
@Update("update user set name = #{name},password = #{password},age = #{age} where id = #{id}")
int updateUserInfo(User user);
}
7.2 注解映射的配置
<mappers>
<!-- 配置具体的映射文件 ,映射xml文件-->
<!-- <mapper resource="cn/zj/mybatis/mapper/UserMapper.xml"/> -->
<!-- 接口映射权限定名 -->
<mapper class="cn.zj.mybatis.mapper.UserMapper"/>
</mappers>
8 方法多参数传递使用-@Param注解
Mybatis默认情况下是不支持传入多个参数的.只能传入一个参数.
所谓的传入参数指定是Mybatis操作(<insert><delete><update><select>
)的传入参数.
方案1:将这些参数封装到一个对象里面(JavaBean/Map),再传入.
方案2:给参数设置一个@Param注解支持,而且多参数的类型要统一
问题:为什么不支持多个参数?
因为Java语法1.7以前.是不能通过反射技术获得方法的参数名的.
解决方案:使用 @Param 参数注解
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import cn.zj.mybatis.domain.User;
public interface UserMapper {
/**
* 模拟登陆操作
* @param user 封装有账号密码的User对象
* @return 查询结果封装的User对象
*/
User login1(@Param("name")String name,@Param("pwd")String password);
}
xml 配置如下:
<select id="login1" parameterType="string" resultType="cn.zj.mybatis.pojo.User">
select * from user where name = #{name} and password = #{pwd}
</select>
9 #{}与${}的区别
在MyBatis框架中支持两种 OGNL语法
#{} 基于 JDBC 的 PreparedStatement 类,SQL 语句参数使用 ? 占位符,在运行阶段动态设置参数,但是 ? 不能作为表名。
预编译语句对象的SQL语句只能 操作 DML和DQL 语句,不能操作DDL语句
- #{}表示设置预编译的参数,就是 ? 的参数,所以如果要不固定的表名不能使用 #{},只能使用 ${}
- ${} 直接把值输出来,直接把参数拼接到 SQL 语句中.而 #{] 是使用 ? 来代替. 所以 ${} 是不安全的.
- ${} 只能获得参数池的值,而 #{} 可以获得方法的参数值,也可以获得参数池的值,如果使用 ${} 获得参数的值,这个参数必须要加上 @Param
如果非必要情况,不要使用${}
问题:那么${}有什么用呢?
答:注意基于JDBC的接口的原来的表名是不可以使用?的,?只能用于传入的参数。如果操作的涉及表名这些非参数的 数据时,需要使用${}