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
在项目的 scr 下面创建 配置文件 mybatis-config.xml
具体配置参考mybatis的官方文档即可
创建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; }}
创建数据库表对应的实体类
package cn.zj.mybatis.pojo;public class User { private Integer id; private String name; private String password; private Integer age; public User() { super(); } public User(Integer id, String name, String password, Integer age) { super(); this.id = id; this.name = name; this.password = password; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", password=" + password + ", age=" + age + "]"; } }
创建一个操作接口
package cn.zj.mybatis.mapper;import cn.zj.mybatis.pojo.User;/* * 使用MyBatis的动态代理开发编写代码遵循四个原则 * 1.映射文件的namespace命名空间的值必须是对应接口的全限定名 2.映射文件的对应功能 id值必须等于映射接口中方法的名称 3.映射文件的参数类型必须和接口中方法的参数类型一致 4.映射文件查询的返回结果类型必须和接口的方法的返回数据类型一致, DML操作返回的受影响的行数,除外 */public interface UserMapper { int insertUserInfo(User u);}
创建表对应的映射文件 :UserMapper.xml
创建测试类准备测试
//插入操作// 新增操作@Testpublic void testInsert() throws Exception { //1.创建SqlSession操作对象 SqlSession session = MyBatisUtil.openSession(); //2.创建UserMapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); //3.创建用户对象 User user = new User(null, "乔峰", "qf", 30); //4.执行UserMapper的插入操作 userMapper.insertUserInfo(user); //5.提交事务 session.commit(); //6.关闭session session.close();}
Mybatis框架配置文件提示问题
MyBatis的配置文件使用dtd约束,如果在没有联网的情况想,默认开发者在使用时没有提示,这样不方便开发者编写代码。通过配置约束文件可让xml文件编写代码有提示
1.联网
2.手动管理DTD约束文件
配置完约束文件以后,关闭xml文件,重新打开即可有提示
log4j日志框架的配置
说明
log4j是一个日志输出框架,就是用于输出日志的。
Mybatis的日志输出是通过Log4J输出的。主流框架大部分都是Log4j输出的。Spring框架也可以通过Log4j输出日志!!
问题:既然Log4j功能类似System.out.println(),为什么使用log4j而不直接使用System.out.println()?
答:Log4j提供了强大的日志输出的自定义功能。
- 通过级别输出日志 (调试、信息、警告、错误、致命异常)
- 可以指定输出到控制台,以及输出到文件。
- 可以设置输出的日志格式
所以学习LOG4J.需要学会自定义配置LOG4J的输出格式以及输出等级
下载路径
Log4j的下载地址:http://logging.apache.org/log4j/1.2/
配置步骤
第一步:导入log4j-1.2.17.jar的包
第二步:在src下创建一个log4j.propertis文件
注意:文件名必须为log4j.properties
# Global logging configurationlog4j.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.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
MyBatis完成CRUD操作
单行查询
映射文件
测试方法
// 单行查询@Testpublic void testFindById() throws Exception { //1.创建SqlSession操作对象 SqlSession session = MyBatisUtil.openSession(); //2.创建UserMapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); //3.执行单行查询操作 User user = userMapper.selectByPrimaryKey(1); System.out.println(user); // 4.关闭session session.close();}
多行查询
映射文件
测试方法
@Testpublic void testFindAll() throws Exception { SqlSession session = MyBatisUtil.openSession(); // 创建UserMaper的代理对象 UserMapper mapper = session.getMapper(UserMapper.class); // 3.执行多行查询 Listusers = mapper.selectAll(); for (User user : users) { System.out.println(user); } // 4.关闭session session.close();}
删除操作
映射文件
delete from user where id = #{id}
测试方法
// 删除操作@Testpublic void testDelete() throws Exception { // 1.创建SqlSession操作对象 SqlSession session = MyBatisUtil.openSession(); // 2.创建UserMapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); // 3.执行UserMapper的插入操作 userMapper.deleteById(3); // 4.提交事务 session.commit(); // 5.关闭session session.close();}
修改操作
映射文件
update user set name = #{name},password = #{password},age = #{age} where id = #{id}
测试方法
// 修改操作@Testpublic void testUpdate() throws Exception { // 1.创建SqlSession操作对象 SqlSession session = MyBatisUtil.openSession(); // 2.创建UserMapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); // 3.创建用户对象 User user = new User(2, "段誉", "dy", 25); // 4.执行UserMapper的插入操作 userMapper.updateUserInfo(user); // 5.提交事务 session.commit(); // 6.关闭session session.close();}
ResultMap 手动映射
MyBatis的查询结果集都是自动映射封装的,单行查询将数据库一条数据封装成对应的Java对象。多行查询,先将每一行封装成对象,再将每个对象添加到集合中,最后返回一个List集合对象。
但是:必须保证查询结果集和pojo对象的属性名相同,否则无法自动封装
问题: 如何解决查询结果集名称和pojo对象属性不同的映射封装?
解决方案:
- 使用手动映射封装 <ResultMap>标签
- 可以使用mybatis的驼峰命名法
主配置文件说明与细节配置
<environments>:环境集标签,就是用于配置数据库的连接信息的
<environment>:用于配置具体环境参数
<transactionManager>:配置使用的事务类型,JDBC
<dataSource>:配置数据源的参数,POOLED
具体参数参看PooledDataSource的set方法
<property>:配置属性
<mappers>:配置映射文件信息的
<mapper class|resource>:配置具体指定的mapper文件
class:配置使用注解时指定有注解的映射接口
resource:指定映射文件
<properties>:mybatis对propertis文件的支持
<typeAliases>:用于配置别名
<typeHandlers>:用于配置自定义类型处理器.
<settings>:配置Mybatis的默认设置的.
总配置文件的标签顺序
这句话的意思就是configuration 标签下的标签的顺序以及标签出现的个数的声明
根据这个声明可以看到顺序为:
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- objectWrapperFactory
- reflectorFactory
- plugins
- environments
- databaseIdProvider
- mappers
DTD规则文件标签的出现的次数说明
如果声明的标签后?:表示出现0-1次
如果声明的标签后*:表示出现0-N次
如果声明的标签后+:表示出现1-N次
如果声明的标签后什么都没有:表示出现1次
别名typeAliases标签
在UserMapper.xml文件中User无论是作为参数还是作为查询返回数据类型,都需要写上全限定名,实际可以写上简单类名即可,但是需要配置别名
MyBatis框架提供了两种别名机制,一种是自定义别名,一种是内置别名
自定义别名
如果配置成功,在映射文件里面可以直接使用别名
insert into user (name,password,age)values(#{name},#{password},#{age}) delete from user where id = #{id} update user set name = #{name} ,password = #{password},age = #{age} where id = #{id}
内置别名
所谓的内置别名,就是Mybatis框架自带别名.
Mybatis已经将常用的数据类型的别名内置声明了.所以这些内置的别名不需要配置就可以直接使用.
内置的别名就是,Mybatis框架默认已经设置的别名.
properties 读取配置文件
一般开发会将单独的数据库连接字符串配置到一个独立的 以 .properties 的配置文件中
Mybaits框架中配置文件 的 <properties>标签可以读取配置文件中的内容。并可以使用${}的语法设置给框架的数据库连接操作代码
在classpath下面创建一个db.properties数据库连接配置文件
jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatisjdbc.username=rootjdbc.password=root
在mybatis-config.xml主配置文件中配置<properties>标签读取配置文件
连接数据库的配置修改为 ${key}的方式
-->
settings标签
Mybatis默认设置了很多默认配置.有时候,我们需求与默认的配置的参数不一样,
我们就需要修改这些默认配置的参数.
如:Mybatis已经对骆驼命名法的支持,但默认是不开启的.可以通过mapUnderscoreToCamelCase参数设置为true支持
表示Mybatis启动可以配置的设置
设置参数 | 描述 | 有效值 | 默认值 |
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true | false | TRUE |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | true | false | FALSE |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods). | true | false | false (true in ≤3.4.1) |
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, FAILING | NONE |
NONE: 不做任何反应 | |||
WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'的日志等级必须设置为 WARN) | |||
FAILING: 映射失败 (抛出 SqlSessionException) | |||
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | Not Set (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。 If allow, set the false. | true | false | FALSE |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。 If allow, set the false. | true | false | TRUE |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | FALSE |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 | true | false | FALSE |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) | true | false | FALSE |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | Any String | Not set |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 or above) |
vfsImpl | 指定VFS的实现 | 自定义VFS的实现的类全限定名,以逗号分隔。 | Not set |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始) | true | false | TRUE |
configurationFactory | Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to load lazy properties of deserialized objects. This class must have a method with a signature static Configuration getConfiguration(). (Since: 3.2.3) | A type alias or fully qualified class name. | Not set |
如支持骆驼命名法
MyBatis的注解开发
MyBatis的映射配置除了使用xml配置以外,还支持注解配置sql语句
问题: 为什么有了xml配置还有注解配置
答 :MyBatis的注解开发更简洁,只需要将对应的SQL语句的注解标注对应的功能方法上即可,直接连 XxxMapper.xml映射文件都可以省略了
本身注解开发就是Java配置的一种趋势,后期学习SpringBoot时候,发现全部用纯注解配置
MyBatis提供了下面注解进行映射文件配置
@Select 查询数据注解@Insert 插入数据注解@Delete 删除数据注解@Update 修改数据注解@Options 选项配置@Results 手动映射配置@Result : @results中的具体的某一列的映射信息配置
案例代码
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;/** * * @Select 查询注解 * @Insert * */public interface UserMapper { @Select("select id u_id,name u_name,password u_password,age u_age from user where id = #{id}") @Results({ @Result(id=true,property="id",column="u_id"), @Result(property="name",column="u_name"), @Result(property="password",column="u_password"), @Result(property="age",column="u_age") }) User selectByPrimaryKey(Integer id); @Select("select * from user") ListfindAll(); @Insert("insert into user (name,password,age)values(#{name},#{password},#{age})") @Options(keyProperty="id",useGeneratedKeys=true) int insertUserInfo(User user); @Delete("delete from user where id = #{id}") int deleteByUserId(Integer id); @Update("update user set name = #{name} ,password = #{password},age = #{age} where id = #{id}") int updateUserInfoById(User user); }
注解映射的配置
方法多参数传递使用-@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(User user); /** * 模拟登陆操作 * @param user 封装有账号密码的Map集合 * @return 查询结果封装的User对象 */ User login2(Mapmap); User login3(@Param("username")String name,@Param("pwd")String password); }
测试代码
@Test public void testlogin3() throws Exception { SqlSession session = MyBatidUtil.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.login3("杨过", "yangguo"); System.out.println(user); }
#{}与${}的区别
在MyBatis框架中支持两种 OGNL语法
#{}基于JDBC的PreparedStatement类,SQL语句参数使用 ?占位符,在运行阶段动态设置参数,但是 ?不能作为表名.
1.#{}表示设置预编译的参数,就是?的参数,所以如果要不固定的表名不能使用#{},只能使用${}
2.${}直接把值输出来,直接把参数拼接到SQL语句中.而#{]是使用?来代替. 所以${}是不安全的.
3.${}只能获得参数池的值,而#{}可以获得方法的参数值,也可以获得参数池的值,如果使用${}获得参数的值,这个参数必须要加上@Param
如果非必要情况,不要使用${}
问题:那么${}有什么用呢?
答:注意基于JDBC的接口的原来的表名是不可以使用?的,?只能用于传入的参数。
如果操作的涉及表名这些非参数的 数据时,需要使用${}
删除案例代码
package cn.zj.mybatis.mapper;import org.apache.ibatis.annotations.Param;public interface UserMapper { /** * 动态删除数据库中指定表名 * @param tableName 表名 * @return */ int dropTable(@Param("tableName")String tableName);}
drop table ${tableName}