一、Mybatis简介
Mybatis:一个持久层框架,支持定制化SQL、存储过程、高级映射,几乎避免了JDBC代码和手动参数以及获取结果集。
数据持久化:
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
内存:断电就丢失
数据库(JDBC):io文件持久化
二、Mybatis实例
思路:搭建环境->导入Mybatis->编写代码->测试
1、编写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核心配置文件 configuration标签-->
<configuration>
<properties resource="db.properties">
<property name="username" value="username"/>
<property name="password" value="password"/>
</properties>
<!-- 事务 、 数据源、 映射文件 -->
<!-- 配置开发环境
default:默认的环境
-->
<!-- 给类配置别名 typeAliases标签-->
<typeAliases>
<!-- type为原名 alias为别名-->
<typeAlias type="pojo.User" alias="User"/>
</typeAliases>
<!-- 如果pojo下类比较多,可以指定包名,在包下自动搜索javabean
扫描到的实体类,默认别名就是类的类名,首字母小写
-->
<typeAliases>
<package name="pojo"/>
</typeAliases>
<!-- 配置环境 environments标签-->
<!-- environments默认名随意 但是要和environment的id值相同-->
<!-- 通过default来切环境 mybatis 默认事务管理器是jdbc 连接池是pooled-->
<environments default="dev">
<!--id值就是默认的环境 -->
<environment id="dev">
<!-- 配置事务管理 选择jdbc事务-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 type:池化的-->
<dataSource type="pooled">
<!-- 配置链接数据库的基本信息-->
<!-- 配置驱动包-->
<property name="driver"
value="com.mysql.jdbc.Driver"/>
<!-- 配置sql服务器地址-->
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<!-- 配置数据库账号-->
<property name="username" value="${username}"/>
<!-- 配置数据库密码-->
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射 每一个mapper都需要再mybatis配置文件中注册-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
2、建立工具类
package until;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUntil {
private static SqlSessionFactory sqlSessionFactory;
private static SqlSession sqlSession;
static {
try {
// mybatis第一步:获取SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis/mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 有了获取SqlSessionFactory对象,自然可以创建出SqlSession的实例
// SqlSession完全包含了执行sql的所有方法
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
3、编写业务代码
①实体类
②Dao接口
package dao;
import pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
}
③接口实现类由原本的UserDaoImpl转变为UserMapper映射文件
<?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绑定一个对应的DAO接口
-->
<mapper namespace="dao.UserDao">
<!-- select查询用户表的所有数据
id对应接口方法名 唯一标识 用id才可以知道进行什么crud操作
resultType要把查询到的结果封装给哪个实体对象
resultMap返回多个
-->
<select id="getUserList"
resultType="pojo.User">
select * from mybatis.user
</select>
</mapper>
三、实现增删改查
用Mapper比用User更灵活
参数的传递:#{}
Map传递参数,直接在sql中取出key
对象传递参数,直接在sql中去对象的属性
只有一个基本类型参数的情况下,直接在sql中取到
<?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绑定一个对应的DAO接口
-->
<mapper namespace="dao.UserDao">
<!-- select查询用户表的所有数据
id对应接口方法名 唯一标识 用id才可以知道进行什么crud操作
resultType要把查询到的结果封装给哪个实体对象 返回多个
resultMap返回1个
parameterType 参数类型
-->
<select id="getUserList"
resultType="pojo.User">
select * from mybatis.user;
</select>
<select id="selectuser"
resultType="pojo.User" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
<insert id="adduser" parameterType="pojo.User">
insert into mybatis.user (id,name,pwd) values (null,#{name},#{pwd});
</insert>
<delete id="deleteuser" parameterType="String">
delete from mybatis.user where name=#{name};
</delete>
<update id="updateuser" parameterType="pojo.User">
update mybatis.user set pwd=#{pwd} where name=#{name};
</update>
<select id="SelectUserLike" parameterType="String" resultType="pojo.User">
select * from mybatis.user where name like #{value};
</select>
</mapper>
package dao;
import pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
User selectuser(int id);
int adduser(User user);
int deleteuser(String name);
int updateuser(User user);
User SelectUserLike(String name);
}
import dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import pojo.User;
import until.MybatisUntil;
import java.util.List;
public class UserTest {
@Test
public void test1(){
// 第一步 获取sqlsession对象
SqlSession sqlSession = MybatisUntil.getSqlSession();
// 方式一 getMapper
UserDao userDao =sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
System.out.println(userList.toString());
// 方式二
List<User> list = sqlSession.selectList("dao.UserDao.getUserList");
System.out.println(list.toString());
sqlSession.close();
}
@Test
public void test2(){
SqlSession sqlSession = MybatisUntil.getSqlSession();
User user = sqlSession.selectOne("dao.UserDao.selectuser",1);
System.out.println(user.toString());
sqlSession.close();
}
@Test
public void test3(){
SqlSession sqlSession=MybatisUntil.getSqlSession();
// 增删改 需要提交事务
int res = sqlSession.insert("dao.UserDao.adduser", new User(1, "liuli", "123"));
sqlSession.commit();
if (res>0){
System.out.println("插入成功!");
}
sqlSession.close();
}
@Test
public void test4(){
SqlSession sqlSession = MybatisUntil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int res = mapper.deleteuser("zhuqi");
if (res>0){
System.out.println("删除成功");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void test5(){
SqlSession sqlSession = MybatisUntil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int res = mapper.updateuser(new User(1, "zhangsan", "987"));
if (res>0){
System.out.println("修改成功!");
}
sqlSession.commit();
sqlSession.close();
}
// 模糊查询
@Test
public void test6(){
SqlSession sqlSession = MybatisUntil.getSqlSession();
List<User> list = sqlSession.selectList("dao.UserDao.SelectUserLike", "z%");
System.out.println(list.toString());
sqlSession.close();
}
}
四、属性优化
在mybatis配置文件 优化属性
<properties resource="db.properties">
<property name="username" value="username"/>
<property name="password" value="password"/>
</properties>
五、设置别名
<!-- 事务 、 数据源、 映射文件 -->
<!-- 第一种、给类配置别名 typeAliases标签-->
<typeAliases>
<!-- type为原名 alias为别名-->
<typeAlias type="pojo.User" alias="User"/>
</typeAliases>
<!-- 第二种、如果pojo下类比较多,可以指定包名,在包下自动搜索javabean
扫描到的实体类,默认别名就是类的类名,首字母小写
-->
<typeAliases>
<package name="pojo"/>
</typeAliases>
第二种方法类名要改变,则需要再实体类中通过注释改变别名
@Alias("user")
public class User {}
基本类型默认类型加_
不加 则是封装类型
例如int 默认别名是_int
六、配置设置
设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled 是否允许单个语句返回多结果集。 true | false true
useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。
NONE: 不做任何反应
WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
FAILING: 映射失败 (抛出 SqlSessionException)
NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。 SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null)
defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null)
safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False
safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false
returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB (3.5.10 起废弃) | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) true | false true
configurationFactory 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一个类型别名或完全限定类名。 未设置
shrinkWhitespacesInSql 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) true | false false
defaultSqlProviderType 指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6). 这个类适用于指定 sql provider 注解上的type(或 value) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider) 类型别名或者全限定名 未设置
nullableOnForEach 为 'foreach' 标签的 'nullable' 属性指定默认值。(新增于 3.5.9) true | false false
argNameBasedConstructorAutoMapping 当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10) true | false false
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="safeResultHandlerEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
<setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
<setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
<setting name="callSettersOnNulls" value="false"/>
<setting name="returnInstanceForEmptyRow" value="false"/>
<setting name="logPrefix" value="exampleLogPreFix_"/>
<setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
<setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
<setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
<setting name="useActualParamName" value="true"/>
<setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>
七、注册映射器
在mybatis配置文件注册绑定mapper文件
方式一:
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
方式二:
<mappers>
<!--用class 要求接口名和xml文件名一样 接口和配置文件在一个包下-->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
方式三:
扫描包进行注册
<mappers>
<package name="dao"/>
</mappers>
八、生命周期和作用域
SqlSessionFactoryBuild:
目的就是为了创建SqlSessionFactory,局部变量
SqlSessionFactory:
类似数据库连接池,用来存会话,创造会话
作用域是应用作用域,一旦创建运行期间一直存在,用单例模式和静态单例模式
SqlSession:
连接到数据库的一个请求,作用域是请求或方法作用域,不能被共享
用完之后需要赶紧关闭,否则资源占用
一个mapper就是一个业务
九、ResultMap结果映射
出现属性名和字段名不一样的情况
1、可以通过在sql中使用as 将字段和属性对应上
<select id="getUserList"
resultType="pojo.User">
select id,name,pwd as password from mybatis.user;
</select>
2、用resultMap解决
哪个属性不一样,映射哪个
<!-- 结果集映射-->
<resultMap id="UserMap" type="pojo.User">
<!-- column代表数据库的字段 property代表实体类的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserList"
resultMap="UserMap">
select id,name,pwd from mybatis.user;
</select>
十、日志
日志最好的用处就是数据库操作出现了异常的时候,进行排除
类似于java的sout和debug
在settings标签中
通过logImpl指定Mybatis所用日志的实现形式,未指定时则自动查找
SLF4J
LOG4J(3.5.9 起废弃)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING 标准日志输出
NO_LOGGING
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J:需要先导包
log4j
properties配置文件
#ERROR:日志级别
#debug,error,Console:日志输出类型
log4j.rootLogger=ERROR,debug,error,Console,File
#打印日志
log4j.appender.Console = org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target = System.out
log4j.appender.Console.ImmediateFlush = true
log4j.appender.Console.Threshold=debug
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern = [%p][%-d{yyyy-MM-dd HH:mm:ss}] %m %n
#输出到日志文件
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=[%-5p] [%d{HH:mm:ss}] %c - %m%n
log4j.appender.debug.datePattern='.'yyyy-MM-dd
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.debug.Threshold=INFO
log4j.appender.debug.append=true
log4j.appender.debug.File=logs/web.info.log
#输出到日志文件
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.layout=org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern=[%-5p] [%d{HH:mm:ss}]%l - %m%n
log4j.appender.error.datePattern='.'yyyy-MM-dd
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.error.Threshold=ERROR
log4j.appender.error.append=true
log4j.appender.error.File=logs/web.error.log
#输出到日志文件
log4j.appender.File = org.apache.log4j.DailyRollingFileAppender
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%-5p] [%d{HH:mm:ss}]%l - %m%n
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.File.Threshold=ERROR
log4j.appender.File.append=true
log4j.appender.File.File = logs/web.file.log
#包级别设置日志级别,rootLogger设置失效
log4j.logger.com.web=INFO
log4j.logger.org.apache.commons.beanutils=ERROR
log4j.logger.org.apache.http=ERROR
最常用的标准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/xu.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
// 在需要使用log4j的类中,导入org.apache.log4j.Logger包 getLogger方法获取Logger 参数为当前类
static Logger logger= Logger.getLogger(UserTest.class);
@Test
public void testlog4j(){
logger.info("hello1");
logger.debug("hello2");
logger.error("hello3");
}