MyBatis笔记从入门到精通

版权声明:已标记,所属内容不得不转载 https://blog.csdn.net/qq_42350238/article/details/82142013
                                        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”));
}
}

猜你喜欢

转载自blog.csdn.net/qq_42350238/article/details/82142013