MyBatis:底层封装了JDBC的持久层框架
1.为什么使用MyBatis
1.1. 传统的ORM框架介绍?
-
传统的访问数据库的方式?JDBC
- 加载驱动 Class.forName()
- 获得连接 Connection con…
- 创建执行SQL的Statement对象 PreparedStatement…
- 执行SQL返回结果 execute executeUpdate executeQuery()
- 处理结果 while rs.next()…
- 关闭资源 close
问题:面向对象开发和面向关系存储之间不匹配问题【不完全对应。】开发人员手动处理由对象到关系型数据库之间的对应。【手动处理】
-
ORM思想的出现:
-
ORM:Object Relational Mapping 对象关系映射,这种思想目标是把面向对象开发中的对象映射到基于SQL的关系型数据库中。
-
addStu(Student stu) orm: addStu(Student stu)
jdbc:手动造车 手动执行 ORM框架.save(stu)–>XML 映射文件
手动处理结果 手动写SQL 映射文件: 类=表 类中属性-表中字段
}
-
-
ORM是一种思想,其中最著名的实现: Hibernate.
-
Hibernate是一款“全自动”的ORM映射框架:实现了Pojo到数据库表的全套映射机制,开发人员只需要定义好映射文件。Hibernate会自动的根据映射文件逻辑生成SQL语句、自动的执行SQL语句、自动的返回结果。
-
Hibernate save()–>insert语句 翻译过程 效率非常低、SQL语句不灵活
2.MyBatis简介?
-
MyBatis也是一款ORM思想的实现框架,底层也是封装的JDBC。
-
MyBatis是一款"半自动的"ORM框架. MyBatis也会执行SQL,返回结果。具体的SQL语句需要开发人员自己写。 灵活 效率高。
-
MyBatis在2010年之前叫IBatis,属于ASF(apache software foundation),后来退出ASF,改名为MyBatis。
-
文档地址:
https://mybatis.org/mybatis-3/zh/configuration.html
-
MyBatis中有两类配置文件:
- 属性文件mybatis-config.xml:连接数据库的信息+属性 配置
- 映射文件StudentMapper.xml: 我们写的SQL语句
3.MyBatis提供的用户接口?
- org.apache.ibatis.session.SqlSessionFactoryBuilder:
- build(IO流)
- build(Configuration 配置对象)
- org.apache.ibatis.session.SqlSessionFactory:
- openSession()
- org.apache.ibatis.session.SqlSession:[核心]
- insert
- update
- delete
- selectOne selectList…
4. MyBatis的helloworld[对学生表的CRUD]?
-
mybatis-config.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"></properties> <settings><!--logImpl:LOG4J 采用log4j做日志--> <setting name="logImpl" value="LOG4J"></setting> </settings> <typeAliases> <!--注册别名 所有需要完成包名+类名的地方都可以通过 别名来替换--> <!--<typeAlias type="com.etoak.student.entity.Student" alias="stu"></typeAlias> <typeAlias type="com.etoak.student.entity.School" alias="sch"></typeAlias>--> <!--给指定的包及其子包起别名 别名默认是:类名字首字母小写 ,可以通过@Alias注解修改默认规则--> <package name="com.etoak.student.entity"/> </typeAliases> <environments default="m"> <environment id="m"> <!--事务管理器:JDBC:代表使用JDBC的方式管理事务 Connection.setAutoCommit(); Connection.commit();/rollback() --> <transactionManager type="JDBC"></transactionManager> <!--POOLED:代表采用MyBatis使用了连接池方式实现的数据源--> <dataSource type="POOLED"> <property name="driver" value="${m.driver}"></property> <property name="url" value="${m.url}"></property> <property name="username" value="${m.user}"></property> <property name="password" value="${m.pwd}"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="StudentMapper.xml" /> </mappers> </configuration>
-
XXMapper.xml
<?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"> <mapper namespace="com.et"> <!-- insert: 执行 insert语句 executeUpdate --> <insert id="addStu" parameterType="com.etoak.student.pojo.Student"> insert into student( name, age, birth, email, schid) values(#{name},#{age},#{birth},#{email},#{schid}) </insert> <!-- select:执行查询 --> <select id="queryStuById" parameterType="int" resultType="com.etoak.student.pojo.Student"> select * from student where id=#{id} </select> <!-- UPDATE executeUpdate --> <update id="updateStu" > update student set name=#{name},age=#{age},birth=#{birth}, email=#{email} where id=#{id} </update> <!-- delete executeUpdate --> <delete id="deleteStu" > delete from student where id=#{id} </delete> <select id="queryAll" resultType="com.etoak.student.pojo.Student"> select * from student </select> </mapper>
-
基础操作
session.insert(SQL语句id,参数)
session.update(SQL语句id,参数)
session.delete(SQL语句id,参数)
session.selectOne(SQL语句id,参数)
session.selectList(SQL语句id,参数)
5. Mapper方式实现MyBatis?
- Mapper.xml中namespace的名字和Mapper接口的名字一致
- Mapper.xml中的SQL语句的id和Mapper接口中方法名字一致
6. 类中属性和表中字段不完全一致?
-
添加
#{类中的属性不是表中的字段}
-
查询
-
起别名
<select id="queryById" parameterType="int" resultType="stu"> select s_id as id, s_name name, s_age age, s_birth birth, s_email as email, s_schid as schid from tb_stu where s_id=#{id} </select>
-
使用resultMap
<resultMap id="rMap_stu" type="stu"> <id property="id" column="s_id"></id> <result property="name" column="s_name"></result> <result property="age" column="s_age"></result> <result property="birth" column="s_birth"></result> <result property="email" column="s_email"></result> <result property="schid" column="s_schid"></result> </resultMap> <select id="queryById" parameterType="int" resultMap="rMap_stu"> select * from tb_stu where s_id=#{id} </select>
-
7. 传递多个参数?
-
Map
-
@Param
-
对象
List<Student> querySome1(@Param("start")int startadadfa,
@Param("pageSize") int pageSizeasdfadf);
List<Student> querySome(Map<String,Object> map);
8. resultType和resultMap的区别?
- resultType:表示执行SQL查询之后的结果集的每一条记录返回的类型,如:自定义的对象Student/User/Teacher、int、map
- resultMap:一定对应的一个元素的id
9. MyBatis如何获得添加到数据库中的主键?
-
JDBC如何获得?
-
MyBatis和JDBC原理一样。
-
使用useGeneratedKeys +keyProperty属性
<insert id="addStudent" parameterType="stu" useGeneratedKeys="true" keyProperty="id" keyColumn="s_id" > insert into tb_stu( s_name, s_age, s_birth, s_email, s_schid) values(#{name},#{age},#{birth},#{email},#{schid}) </insert>
-
selectKey
<insert id="addStudent" parameterType="stu" > <selectKey keyProperty="id" keyColumn="s_id" resultType="int" order="AFTER"> select last_insert_id() <!-- select replace(uuid(),'-','')--> </selectKey> insert into tb_stu( s_name, s_age, s_birth, s_email, s_schid) values(#{name},#{age},#{birth},#{email},#{schid}) </insert>
-
10. 批量添加?
-
使用就是动态标签 foreach
<insert id="addStus1" parameterType="list"> insert into tb_stu(s_name, s_age, s_birth, s_email, s_schid) values <foreach collection="list" item="s" separator=","> (#{s.name},#{s.age},#{s.birth},#{s.email},#{s.schid}) </foreach> </insert> <insert id="addStus" parameterType="list"> insert into tb_stu(s_name, s_age, s_birth, s_email, s_schid) <foreach collection="list" item="s" separator=" union "> (select #{s.name},#{s.age},#{s.birth},#{s.email},#{s.schid} ) </foreach> </insert>
11. 模糊查询:#和$的区别?
1.#底层使用的是预编译的Statement,即:PreparedStatement,支持?占位符的。$:底层使用的是普通的Statement,不支持?占位符的,参数只能拼接。
2.有些情况不能使用?占位:
参考JDBC :PrearedStatement和Statement的区别。
12. MyBatis中的动态SQL标签?
-
where
-
if
<select id="queryByConditions1" resultMap="rMap_stu"> select * from tb_stu <where> <if test="name!=null and name!='' "> and s_name like '%${name}%' </if > <if test="email!=null and email!='' "> and s_email=#{email} </if> </where> </select>
-
foreach
<insert id="addStus1" parameterType="list"> insert into tb_stu(s_name, s_age, s_birth, s_email, s_schid) values <foreach collection="list" item="s" separator=","> (#{s.name},#{s.age},#{s.birth},#{s.email},#{s.schid}) </foreach> </insert> <insert id="addStus" parameterType="list"> insert into tb_stu(s_name, s_age, s_birth, s_email, s_schid) <foreach collection="list" item="s" separator=" union "> (select #{s.name},#{s.age},#{s.birth},#{s.email},#{s.schid} ) </foreach> </insert>
-
trim
<select id="queryByConditions" resultMap="rMap_stu"> select * from tb_stu <!--prefix:前缀 prefixOverrides:前边遇到什么内容干掉--> <trim prefix="where" prefixOverrides="and"> <if test="name!=null and name!='' "> and s_name like '%${name}%' </if > <if test="email!=null and email!='' "> and s_email=#{email} </if> </trim> </select>
-
chose…when.otherwise
<select id="queryByConditions" resultMap="rMap_stu"> select * from tb_stu <!--prefix:前缀 prefixOverrides:前边遇到什么内容干掉--> <trim prefix="where" prefixOverrides="and"> <choose> <when test="name!=null and name!='' "> and s_name like '%${name}%' </when> <otherwise> and s_email=#{email} </otherwise> </choose> </trim> </select>
13. 一对一和一对多的关联查询?
-
association
<resultMap id="rMap_stu_sch" type="stu"> <id property="id" column="s_id"></id> <result property="name" column="s_name"></result> <result property="age" column="s_age"></result> <result property="birth" column="s_birth"></result> <result property="email" column="s_email"></result> <result property="schid" column="s_schid"></result> <!--association:一对一关联 property:类中的属性 javaType:属性的类型--> <association property="sch" javaType="com.etoak.student.entity.School"> <id property="id" column="id"></id> <result property="name" column="name"></result> <result property="phone" column="phone"></result> <result property="info" column="info"></result> </association> </resultMap> <select id="queryByIdWithSch" resultMap="rMap_stu_sch"> select s_id, s_name, s_age, s_birth, s_email, s_schid, sch.id, sch.name, sch.phone,sch.info from tb_stu s left join school sch on s.s_schid = sch.id where s.s_id=#{id} </select>
-
collection:一对多
<resultMap id="rMap_sch" type="com.etoak.student.entity.School"> <id property="id" column="id"></id> <result property="name" column="name"></result> <result property="phone" column="phone"></result> <result property="info" column="info"></result> <!-- collection:表示集合 List /set都可以 property:类中的属性 ofType:集合中每一个元素的类型 --> <collection property="stus" ofType="stu"> <id property="id" column="s_id"></id> <result property="name" column="s_name"></result> <result property="age" column="s_age"></result> <result property="birth" column="s_birth"></result> <result property="email" column="s_email"></result> <result property="schid" column="s_schid"></result> </collection> </resultMap> <select id="querySchWithStus" resultMap="rMap_sch"> select sch.id, sch.name, sch.phone,sch.info, s_id, s_name, s_age, s_birth, s_email, s_schid from school sch left join tb_stu s on s.s_schid = sch.id where sch.id=#{id} </select>
14. MyBatis的核心接口【面向内部的】?
-
org.apache.ibatis.session.Configuration:
- 代表MyBatis中的配置文件
- 其中的属性:Environment:代表环境标签
- 其中的属性:MappedStatement:代表SQL映射文件中的等标签
-
org.apache.ibatis.mapping.Environment:
- 代表环境元素.
- 其中的属性:id+transactionFactory+dataSource
-
org.apache.ibatis.mapping.MappedStatement:
-
代表SQL映射文件中的标签
-
其中的属性:
-
StatementType:执行器的类型 枚举
STATEMENT:普通的执行器 PREPARED:预编译的执行器 CALLABLE:执行触发器的执行器
-
ResultSetType:结果集的类型 枚举
DEFAULT(-1):默认的结果集 FORWARD_ONLY(1003):不可滚动的结果集 SCROLL_INSENSITIVE(1004):可滚动结果集 SCROLL_SENSITIVE(1005):可滚动的结果集
-
SqlCommandType:SQL语句的类型 枚举
INSERT/UPDATE/DELETE/SELECT/UNKNOWN
-
SqlSource:解析SQL语句的方法+获得SQL的方法
其中有一个方法getBoundSql:BoundSql真正代表SQL语句
-
-
-
org.apache.ibatis.type.TypeAliasRegistry:类型别名注册器 存放别名
-
TypeAliasRegistry构造时默认注册一些别名: int string list
-
new Configuration():中 自动添加别名 如:JDBC POOLED LOG4J
-
我们写的别名
1. <typeAlias type="" alias=""> 2. <typeAliasPackage value="">
-
-
org.apache.ibatis.builder.BaseBuilder:基本的解析器
-
代表解析器。解析XML的。
-
其中主要属性和构造方法
public abstract class BaseBuilder{ protected final Configuration configuration; protected final TypeAliasRegistry typeAliasRegistry; protected final TypeHandlerRegistry typeHandlerRegistry; public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }
-
BaseBuilder有很多子类:
- XMLConfigBuilder:解析 mybatis-config.xml
- XMLMapperBuilder:解析 *Mapper.xml
- XMLStatementBuilder:解析insert update delete标签的
- XMLScriptBuilder:SQL语句
-
-
org.apache.ibatis.io.Resources:资源
- 该类是MyBatis封装的工具类。专门加载配置文件
- 底层委托 classLoaderWrapper实现具体加载的。原理就是使用ClassLoader.getResourceAsStream实现的。
15. 解析配置以及SqlSessionFactory构造的过程?
XMLPathParser:接收mybatis-config.xml流+resolver,本身提供把流转换成Document对象【JDK中提供DOM解析器】的方法和一堆使用JDK中提供的XPath方式计算XML中每一个部分的的方法。
-
分析步骤2: parser.parse():挨个解析mybatis-config和mapper.xml中的每一个元素 最终都装configuration对象
-
步骤分析3:new DefaultSqlSessioFacotry(config)
16. parseConfiguration中的几个部分分析?
-
XMLConfigBuilder:解析mybatis-config.xml
1.XMLConfigBuilder中 调用 XMLMapperBuilder解析 Mapper.xml,
2.然后再XMLMapperBuilder 调用XMLStatementBuilder专门解析元素中的每一个属性和内容
2.然后再XMLStatementBuilder中调用XMLScriptBuilder 构造SQL语句 BoundSql===》SqlSource
17. 常见错误?
- Mapped Statements collection does not contain value for xxx
原因: SQL语句映射文件中没有DAO层调用的语句。
-
A query was run and no Result Maps were found for the Mapped Statement ‘com.et2203.queryById’. It’s likely that neither a Result Type nor a Result Map was specified.
原因:执行的是查询语句,没有resultType或者resultMap。
-
Caused by: org.apache.ibatis.binding.BindingException: Parameter ‘start’ not found. Available parameters are [arg1, arg0, param1, param2]
原因:传递多个参数MyBatis默认不会按照形参去命名,而是自动命名为:arg0,arg1…或者param1 param2…