MyBatis笔记从入门到精通
什么是框架
概念:
框架是可被应用开发者定制的应用骨架.
简单记,就是别人写好了一部分代码, 我们在此基础上进行开发. 别人写好的这个部分代码就称为框架.
为什么使用框架:
让开发更加便捷
项目的三层架构:
1.表现层 <controller>
{springmvc/struts2}
2.业务逻辑层 <service>
3.数据持久层<dao>
mybaits/hibernate
框架架构讲解:
(1)加载配置:
配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个
mybatis结构个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:
当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应 的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3)SQL执行:
将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:
将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,封装并将最终结果返回。
MyBatis的入门案例<超重点>
1.在pom.xml文件中引入相关的依赖
2.编写user实体类 User
3.编写持久层接口 以及方法 list<user> findAll();
4.编写持久层接口的 映射文件
<?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.itheima.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>
注意: 位置 : 必须和持久层接口在相同的包下
命名: 必须以持久层接口命名文件名,扩展名是.xml
5.编写 SqlMapConfig.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>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接数据库的信息:用的是数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ee50"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映
射配置的位置 –>
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
核心
配置文件的细节:
1.配置了数据库的相关信心
2.配置了映射文件的
public static void main(String[] args)throws Exception {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
//6.释放资源
session.close();
in.close();
}
映射文件详解
传入参数 parameterType
基本数据类型+ String pojo对象 map
传出参数 resultType
基本类型 实体类
resultmap<字段与实体类的属性不一致>
映射文件中
!– 建立 User 实体和数据库表的对应关系 type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称
<!-- 配置查询所有操作 -->
resultMap的值是我们定义的resultMap标签的id
<select id="findAll" resultMap="userMap">
select * from user
</select>
#{}与${}的区别
#
{}表示一个占位符号 相当于 ?
${}表示拼接 sql 串
SqlMapConfig.xml配置文件
1properties(属性)
一般用于配置数据库的4大金刚
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="1234"/>
</properties>
2 ty peAliases(类型别名)
<package name="com.itheima.domain"/>
3.mappers(映射器)
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
连接池
UNPOOLED 不适用连接池的数据源
POOLED 使用连接池的数据局源
JNDL 使用JNDI实现的数据源
注释:以后我们和spring整合后,可以使用其他第三方的连接池 如c3p0
事物
1.什么是事物:单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行
简单说: 一个逻辑操作中的所有sql语句 , 要么执行成功要么失败
2.不考虑事物的隔离性出现问题
脏读
不可重复读
幻读
3.自动提交事务
sqlSession = factory.openSession(true);
总结 多表查询之间的关系
一对一 实体类中 有一个user对象
一对多 实体类中 有一个user集合
一对多 实体类中 有一个user集合
多对一 实体类中 有一个user集合
这两个加在一起就是多对多的关系
配置文件(表单查询)
多表查询(主要是看后面的一 还是 多 )
多对一{一对一}
实体类
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//从表实体应该包含一个主表实体的对象引用
private User user;
…get/set方法
映射文件
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<!-- <id property="id" column="aid"></id>-->
<result property="id" column="aid"></result>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容-->
<association property="user"(属性) column="uid" javaType="user">(对应类型)
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<!-- 查询所有
–>
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
</select>
}
一对多 (多对多)
实体类
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
// 1….. 一对多关系映射:主表实体应该包含从表实体的集合引用
private List accounts; // Set
….get/set方法
映射文件
<!-- 定义User的resultMap 2......封装user 以及user里面的account-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" (集合属性) ofType="account"> (对应类型)
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
```
<!--
查询所有–
,3…….我们想到了左外连接查询比较合适。
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
详情:http://www.cnblogs.com/yyjie/p/7788413.html;
–>
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
</select>
}
Mybatis 的动态 SQL 语句
1.if
<select id="findByUser" resultType="user" parameterType="user">
select * from user where 1=1
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</select>
注意:if>
标签的 test 属性中写的是对象的属性名
<select id="findByUser" resultType="user" parameterType="user">
<include refid="defaultSql"></include>
<where>
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</where>
</select>
3.foreach
主要解决:
sql语句中in (1,2,3)的情况
实体类
public class QueryVo implements Serializable {
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
-------
映射文件
<select id="findInIds" resultType="user" parameterType="queryvo">
<!-- select * from user where id in (1,2,3,4,5); -->
<include refid="defaultSql"></include>
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="id in ( " close=")" item="uid"
separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
SQL 语句:
select 字段 from user where id in (?)
<foreach>
标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素,注意编写时不要写#{}(就是实体类中的 list 集合)
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名(随便起 item=”uid”,但是要和 后面取值的变量一致 #{uid} )
sperator:代表分隔符 * ( 一般用 , 号 )
====================================
sql片段
抽取代码块片段
<sql id="defaultSql">
select * from user
</sql>
引 用代码块片段
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>
<
!– 根据 id 查询 –>
<select id="findById" resultType="UsEr" parameterType="int">
<include refid="defaultSql"></include>
where id = #{uid}
</select>
MyBatis延迟加载
1 延迟加载和立即加载的概念
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
总是在加载用户信息时就一定要加载他的账户信息。
2 mybatis一对一实现延迟加载
老规矩:1.sql语句,2.pojo类,3.mapper.xml和mapper.java接口
使用 association实现延迟加载 一对一
使用 Collection 实现延迟加载 一对多
pojo类
public class Orders {
private String number;
private Date createtime;
private String note;
//用户信息,新增了一个User属性,为了保存查询得到的关联的User表的信息(一对一)
private User user;
映射文件
resultMap type=”user” id=”userMap”>
id column=”id” property=”id”> !– collection 是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型
select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
column 是用于指定使用哪个字段的值作为条件查询
迟加载的开关
在SqlMapConfig.xml中
<!-- 打开延迟加载 的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
- 开启二级缓存
setting >name=”cacheEnabled”value=”true”/>
执行上边mapper方法(findOrdersUserLazyLoading),内部去调用com.iot.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查询orders信息(单表)。
在程序中去遍历上一步骤查询出的List,当我们调用Orders中的getUser方法时,开始进行延迟加载。
延迟加载,去调用UserMapper.xml中findUserbyId这个方法获取用户信息。
3 mybatis一对多实现延迟加载(同上)
MyBatis缓存
1 缓存的概念
像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提 高性能。
2 Mybatis中的一级缓存
默认是存在并且是开启的
3 触发清空一级缓存的情况
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存
4 mybatis的二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
二级缓存的开启与关闭
第一步:在 SqlMapConfig.xml 文件开启二级缓存
<settings>
<!-- 开启二级缓存的支持 --> <setting name="cacheEnabled" value="true"/> </settings>
第二步:配置相关的 Mapper 映射文件
<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
<?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.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 --> <cache></cache> </mapper>
第三步:配置 statement 上面的 useCache 属性
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用 二级缓存,如果不使用二级缓存可以设置为 false。 注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
注解开发(表单查询);
常用注释:
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
1.表单操作
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
List<User> findAll();
/*** 根据 id 查询一个用户
* @param userId
* @return
*/
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
/**
* 保存操作
* @param user
* @return
*/
@Insert("insert into
user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address}
)")
@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before =
false, statement = { "select last_insert_id()" })
int saveUser(User user);
/**
* 更新操作
* @param user
* @return
*/
@Update("update user set
username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id
=#{id} ")
int updateUser(User user);
/**
* 删除用户
* @param userId
* @return
*/
@Delete("delete from user where id = #{uid} ")
int deleteUser(Integer userId);
/**
* 查询使用聚合函数
* @return
*/
@Select("select count(*) from user ")
int findTotal();
/**
* 模糊查询
* @param name
* @return
*/
@Select("select * from user where username like #{username} ")
List<User> findByName(String name);
}
注意:
1. 在一个接口中有一个方法定义了resluts其他的就可以直接使用了
2. 通过注解方式,我们就不需要再去编写 UserDao.xml 映射文件了。
2.一对一
Account
public interface IAccountDao { Account 有一个 User 对象
/**
* 查询所有账户,采用延迟加载的方式查询账户的所属用户
* @return
*/
@Select("select * from account")
@Results(id="accountMap",
value= {
@Result(id=true,column="id",property="id"),
@Result(column="uid",property="uid"),
@Result(column="money",property="money"),
@Result(column="uid", // uid值为要传入findByid 的参数
property="user",
one=@One(select="com.itheima.dao.IUserDao.findById",
fetchType=FetchType.LAZY)
)
})
List<Account> findAll();
User
public interface IUserDao {
/**
* 根据 id 查询一个用户
* @param userId
* @return
*/
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
}
3.一对多
Account
public interface IAccountDao {
/**
* 根据用户 id 查询用户下的所有账户
* @param userId
* @return
*/
@Select("select * from account where uid = #{uid} ")
List<Account> findByUid(Integer userId);
}
User
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday"),
@Result(column="id",property="accounts", // id 值为要传入findByUid的参数
many=@Many(
select="com.itheima.dao.IAccountDao.findByUid",
fetchType=FetchType.LAZY
)
)
})
List<User> findAll();
}
@Many:
相当于<collection>的配置
select 属性:代表将要执行的 sql 语句
fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值
Dom4j解析xml
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<bookname id="10001">红与黑</bookname>
<price>12.3</price>
<author>列夫</author>
</book>
<book>
<bookname>三个火枪手</bookname>
<price>34</price>
<author>佚名</author>
</book>
<book>
<bookname>钢铁是怎样炼成的</bookname>
<price>21</price>
<author>列夫托尔斯泰</author>
</book>
</books>
解析
/**
* Describe:
* Copyright@ 2018/8/28
* <p>
* --- ONE Goal,One Passion !
* SAX解析一般使用:
*
* 1. 将一个xml文件读取为一个Document 对象
* Document document = new SAXReader().read(in)
*
* 2. 获取节点 . 根据xpath表达式来选择节点
*
* 2.1. Element elements = document.selectNodes("xpath表达式")
* 2.2 Element elements = element.selectNodes("xpath表达式")
*
* 3. 获取节点的信息
*
* a.获取节点的属性值 <book id="1">
* 节点.attributeValue("id")
* b. 获取节点内文本内容 <author>列夫</author>
*
*/
public class SaxReadTest {
@Test
public void readData() throws Exception {
InputStream in = Resources.getResourceAsStream(“books.xml”);
//2.根据字节输入流获取Document对象
SAXReader reader = new SAXReader();
Document document = reader.read(in);
// 获取根节点
Element rootElement = document.getRootElement();
// 选择节点
List<Element>
selectNodes = rootElement.selectNodes(“//bookname”);
System.out.println(selectNodes.get(0).getText());// 获取标签文本
System.out.println(selectNodes.get(0).attributeValue(“id”));
}
}