11-MyBatis01

今日知识

1. MyBatis简介
2. MyBatis入门
3. 全局配置文件其他配置
4. MyBatis的映射文件
5. 动态SQL
6. mybatis和hibernate区别

MyBatis简介

1. MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。 
2. MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开
发者只需要关注 SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、
创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
3. 原理
    1. Mybatis通过xml或注解的方式将要执行的各种statementstatementprepared
    Statemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进
    行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

MyBatis入门

1. 导入依赖包和核心包

* <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.6</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.7</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.30</version>
    </dependency>

2. 添加log4j.properties文件到resources,用于打印日志信息

* # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3. 创建pojo类User对象,创建数据库和表

4. 创建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="development">
            <environment id="development">
                <!-- 配置JDBC事务控制,由mybatis进行管理 -->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置数据源,采用dbcp连接池 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatisday01?useUnicode=true&amp;characterEncoding=utf8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <!--告诉mybatis加载映射文件-->
        <mappers>
            <mapper resource="User.xml"/>
        </mappers>
    </configuration>

5. 创建映射文件User.xml在resources文件中

* <?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">
    <!-- 
    	namespace:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
    	注意:使用mapper代理开发时,namespace有特殊且重要的作用
     -->
    <mapper namespace="test">
    	<select id="findUserById" parameterType="int" resultType="com.rqy.model.User">
        SELECT  * FROM  USER WHERE  id=#{id}
        </select>
        <select id="findUserByName" parameterType="String" resultType="com.rqy.model.User">
        SELECT  * FROM  USER WHERE  username=#{username}
        </select>
    </mapper>

6. SqlMapConfig.xml配置文件加载映射文件

* <!--告诉mybatis加载映射文件-->
        <mappers>
            <mapper resource="User.xml"/>
        </mappers>

7. 测试类

* public class MyTest {
    @Test
    public void test() throws IOException {
        //a)	读取配置文件;
        InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
        //b)	通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
        //c)	通过SqlSessionFactory创建SqlSession。
        SqlSession sqlSession = ssf.openSession();
        //d)	调用SqlSession的操作数据库方法。
        User user = sqlSession.selectOne("findUserByName", "张三");
        System.out.println(user);
        //e)	关闭SqlSession。
        sqlSession.close();
    }
}

更多案例讲解

1. 模糊查询用户信息

* <!-- 
	[${}]:表示拼接SQL字符串
 	[${value}]:表示要拼接的是简单类型参数。
	 注意:
	1、如果参数为简单类型时,${}里面的参数名称必须为value 
	2、${}会引起SQL注入,一般情况下不推荐使用。但是有些场景必须使用${},比如order by ${colname}
-->
<select id="findUserByName" parameterType="String" resultType="com.rqy.domain.User">
	SELECT * FROM USER WHERE username like '%${value}%'
</select>

2. 插入用户信息

* <insert id="insertUser" parameterType="user">
    INSERT into USER  (username,birthday,sex,address) VALUES  (#{username},#{birthday},#{sex},#{address})
</insert>
*  @Test
public void test1() {
//d)	调用SqlSession的操作数据库方法。
User user=new User("任清阳","1",new Date(),"神州");
int insertUser = sqlSession.insert("insertUser", user);
sqlSession.commit();//默认事务不会给我提交
System.out.println("返回结果:"+insertUser);

}

3. 删除用户

* <delete id="deleteUser" parameterType="int">
    DELETE FROM USER WHERE id=#{id}
</delete>

4. 修改用户

*  <update id="updateUser" parameterType="user">
    UPDATE USER SET  username=#{username},sex=#{sex},WHERE  id=#{id}
</update>
*  @Test
public void test2() {
//d)	调用SqlSession的操作数据库方法。
User user=new User();
user.setId(34);
user.setUsername("任意");
user.setSex("2");
int result = sqlSession.insert("updateUser", user);
sqlSession.commit();//默认事务不会给我提交
System.out.println("返回结果:"+result);

}

5. 主键返回之MySQL自增主键

* <!-- 
	[selectKey标签]:通过select查询来生成主键
	[keyProperty]:指定存放生成主键的属性
	[resultType]:生成主键所对应的Java类型
	[order]:指定该查询主键SQL语句的执行顺序,相对于insert语句
	[last_insert_id]:MySQL的函数,要配合insert语句一起使用 -->
* <selectKey keyProperty="id" resultType="int" order="AFTER">
		SELECT LAST_INSERT_ID()
	</selectKey>
* @Test
public void test3() {
//d)	调用SqlSession的操作数据库方法。
User user=new User("任清阳","1",new Date(),"神州");
int insertUser = sqlSession.insert("insertUser2", user);
sqlSession.commit();//默认事务不会给我提交
System.out.println("返回结果:"+insertUser);
System.out.println("id:"+user.getId());
}

6. 主键返回之MySQL自增UUID

* <selectKey keyProperty="id" resultType="String" order="BEFORE">
		SELECT UUID()
	</selectKey>

7. 小结

1. parameterType和resultType
    parameterType指定输入参数的java类型,可以填写别名或Java类的全限定名。
    resultType指定输出结果的java类型,可以填写别名或Java类的全限定名。
2. #{}和${}
    #{}:相当于预处理中的占位符?。
    #{}里面的参数表示接收java输入参数的名称。
    #{}可以接受HashMap、POJO类型的参数。
    当接受简单类型的参数时,#{}里面可以是value,也可以是其他。
    #{}可以防止SQL注入。
    ${}:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
    ${}会引起SQL注入,所以要谨慎使用。
    ${}可以接受HashMap、POJO类型的参数。
    当接受简单类型的参数时,${}里面只能是value。
3. selectOne和selectList
    selectOne:只能查询01条记录,大于1条记录的话,会报错:
    selectList:可以查询0N条记录

MyBatis的Dao编写

1. mapper代理方式实现

Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可。
Mybatis会自动的为mapper接口生成动态代理实现类。

2. 实现mapper代理开发模式需要遵循一些开发规范

1.	mapper接口的全限定名要和mapper映射文件的namespace的值相同。
2.	mapper接口的方法名称要和mapper映射文件中的statement的id相同;
3.	mapper接口的方法参数只能有一个,且类型要和mapper映射文件中statement的parameterType的值保持一致。
4.	mapper接口的返回值类型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致;

通过规范式的开发mapper接口,可以解决原始dao开发当中存在的问题:
    * 模板代码已经去掉;
    * 剩下去不掉的操作数据库的代码,其实就是一行代码。这行代码中硬编码的部分,通过第一和第二个规范就可以解决。

3.编写步骤

1. 重新写个UserMapper配置文件和定义mapper映射文件UserMapper.xml(内容同Users.xml,除了namespace的值),放到新创建的目录mapper下。
1. UserMapper接口创建
public interface UserMapper {
public int insertUser(User user);
public User findUserById(int id);
}
2. UserMapper.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.rqy.mapper.UserMapper">
    <insert id="insertUser" parameterType="user">
        INSERT into USER  (username,birthday,sex,address) VALUE  (#{username},#{birthday},#{sex},#{address})
    </insert>
    <select id="findUserById" parameterType="int" resultType="com.rqy.model.User">
        SELECT * FROM USER WHERE id=#{id}
    </select>
</mapper>
2. 第二步:【添加映射配置文件】
<mappers>
    <mapper resource="UserMapper.xml"/>
</mappers>
3. 测试
public class MyTest3 {
SqlSession sqlSession=null;
@Before
public void before() throws IOException {
   //a)	读取配置文件;
   InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
   //b)	通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
   SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
   //c)	通过SqlSessionFactory创建SqlSession。
    sqlSession = ssf.openSession();
}
@After
public void after(){
   //e)	关闭SqlSession。
   sqlSession.close();
}
@Test
public void test1() {
    //1. 通过session获取UserMapper的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //2. 创建一个用户
    User user=new User("任清阳","1",new Date(),"神州");

    //3. 保存用户
    int insertUser =userMapper.insertUser(user);
    System.out.println("返回结果:"+insertUser);
    //4.查询用户信息
    User userById = userMapper.findUserById(34);
    System.out.println("userById:"+userById);
    sqlSession.commit();//默认事务不会给我提交
}
}

全局配置文件其他配置

1. properties数据库文件配置
    * 在resources配置个db.properties文件
    * driverClass=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatisday01?useUnicode=true&amp;characterEncoding
    username==root
    password=123456
    * SqlMapConfig.xml中配置<properties resource="db.properties"/>
    * <property name="driver" value="${driverClass}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
2. setting[了解]
3. typeAliases
    * 别名是使用是为了在映射文件中,更方便的去指定参数和结果集的类型,不再用写很长的一段全限定名。
    * 实例:在sqlMappingConfig.xml的<configuration>标签中写
         <!--别名配置-->
    <typeAliases>
        <!--单个定义别名-->
        <typeAlias type="com.rqy.model.User" alias="user"/>
        <!--批量配置别名
        [name],指定批量定义别名的类包,别名为类名(首字母大小写都可以)
        -->
        <package name="com.rqy.model"/>
    </typeAliases>
    * 在User.xml中可以设置parameterType为user, 书写简单
4. mappers:诉mybatis加载映射文件,几种方法
    1. 方式一:<mapper resource="com/rqy/mapper/UserMapper.xml"/>
    2. 方式二: <mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
    3. 方式三:<mapper class="com.rqy.mapper.UserMapper"/>
        * 与类名相同,一定要有映射文件相对应,此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下
    4. 方式四:<package name="com.rqy.mapper"/>
        * 注册指定包下的所有映射文件
        * 此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下
    5. 可使用注解开发,把xml文件删除(不推荐)
        * 在对应的接口方法上加
        @insert("sql语句")
        @select("sql语句")

MyBatis的映射文件

1. 输入映射ParameterType

1. 传递简单类型:int
    * SELECT * FROM USER WHERE id=#{?}
2. 传递POJO对象
    *  <insert id="insertUser" parameterType="user">
    INSERT into USER  (username,birthday,sex,address) VALUE  (#{username},#{birthday},#{sex},#{address})
</insert>
3. 传递POJO包装对象
    *  <insert id="insertUser" parameterType="userMapper">
    INSERT into USER  (username,birthday,sex,address) VALUE  (#{user.username},#{user.birthday},#{user.sex},#{user.address})
     </insert>
    * user指向的是UserMapper对象属性
    *  <!--别名配置-->
        <typeAliases>
            <!--单个定义别名-->
            <typeAlias type="com.rqy.model.User" alias="user"/>
            <!--批量配置别名
            [name],指定批量定义别名的类包,别名为类名(首字母大小写都可以)
            -->
            <package name="com.rqy.model"/>
        </typeAliases>
4. 传递Map对象
    *  public List<User> findUserListByMap(Map<String,Object> map);
    *      <select id="findUserListByMap" parameterType="hashmap" resultType="user">
    SELECT * FROM USER WHERE username LIKE '%${username}%'
</select>

2. 输出映射 resultType/resultMap

1. resultType
    * 使用resultType进行结果映射时,查询的列名和映射的pojo属性名完全一致,该列才能映射成功。
    * 如果查询的列名和映射的pojo属性名全部不一致,则不会创建pojo对象;
    * 如果查询的列名和映射的pojo属性名有一个一致,就会创建pojo对象。
    1. 输出简单类型
        *  当输出结果只有一列时,可以使用ResultType指定简单类型作为输出结果类型,
    2. 输出PoJo单个对象和pojo列表时,resultType的类型是一样,mapper接口方法的返回值不同
2. resultMap
    1. 如果查询出来的列名和属性名不一致时,通过定义一个resultMap
    将列名和pojo属性名之间作一个映射关系。
    * 定义resultMap
    * 使用resultMap作为statement的输出映射类型
    * UserMapper.java
    步骤一://返回结果为ResultMap
     public User findUserByResultMap(int id);
    步骤二:
    <!--返回结果为ResultMap-->
    <resultMap id="userResultMap" type="user">
        <id column="id_" property="id"/>
        <result column="username_" property="username"/>
        <result column="sex_" property="sex"/>
    </resultMap>
    <select id="findUserByResultMap" parameterType="int" resultMap="userResultMap">
        SELECT id id_,username username_,sex sex_ FROM USER WHERE id = #{?}
    </select>
    测试:System.out.println(userMapper.findUserByResultMap(1));

动态SQL

1. if和where
1. if标签:用作判断入参来使用,如果符合条件,则把if标签体内的sql拼接上,
    * 注意:用if进行判断是否为空时,不仅要判断null,也要判断空字符串‘’;
    * 大于号用:&gt;
    * 小于号用:&lt;
    * 字符串使用单引号:''
    * 
2. Where标签:会去掉条件中的第一个and符号。
3. 测试:
    SELECT * FROM USER
    <where>
        <if test="user!=null">
            <if test="user.username !=null and user.username !=''">
                username LIKE '%${user.username}%'
            </if>
            <if test="user.sex != null and user.sex !=''">
                and sex=#{user.sex}
            </if>
        </if>
    </where>
2. SQL片段
1. Mybatis提供了SQL片段的功能,可以提高SQL的可重用性。
2. 测试
3. <!--SQL片段-->
<sql id="select_user_where">
    <if test="user!=null">
        <if test="user.username !=null and user.username !=''">
            username LIKE '%${user.username}%'
        </if>
        <if test="user.sex != null and user.sex !=''">
            and sex=#{user.sex}
        </if>
    </if>
</sql>
<select id="findUserList" parameterType="userQuery" resultType="user">
    SELECT * FROM USER
    <where>
        /*引用sql片段*/
        <include refid="select_user_where"/>
    </where>
</select>
3. foreach遍历
1. 查询指定id的用户(32,33,34)list类型 
2. <select id="findUserByIds" parameterType="userQuery" resultType="user">
    SELECT * FROM USER
    <where>
        <if test="ids!=null and ids.size > 0">
         <!--foreach标签
         collection:集合参数的名称,如果直接传入集合参数,则该处的参数只能写【list】
         item:每次遍历出来的对象
         open:开始遍历时拼接的串
         close:结束遍历时拼接的串
        separator:每次遍历对象之间要加的分割符
         -->
            <foreach collection="ids" item="id" open="and id in (" close=")" separator=",">
                ${id}
            </foreach>
        </if>
    </where>
</select>
3. List无注解
    int insertUsers(List<User> users)
    <insert id="insertUsers">
        intsert into user (username,password,age,emali) values
        <foreach collection="list" item="user"  separator=",">
                (${user.username},#{user.password},#{user.age},#{user.email})
        </foreach>
    </insert>
4. List无注解,collection填的有变化
    int insertUsers(@Param("users")List<User> users)
    <insert id="insertUsers">
        intsert into user (username,password,age,emali) values
        <foreach collection="users" item="user"  separator=",">
                (${user.username},#{user.password},#{user.age},#{user.email})
        </foreach>
    </insert>
5.  当无注解时,数组和List在collection属性分别填array和list;
    当包含@Param注解时collection中填@Param注解中的内容

mybatis和hibernate区别(面试题)

1. MyBatis技术特点:
    * 好处
        1. 通过直接编写SQL语句,可以直接对SQL进行性能的优化;
        2. 学习门槛低,学习成本低。只要有SQL基础,就可以学习mybatis,而且很容易上手;
        3. 由于直接编写SQL语句,所以灵活多变,代码维护性更好。
    * 缺点
        1. 不能支持数据库无关性,即数据库发生变更,要写多套代码进行支持,移植性不好。
            * Mysql:limit
            * Oracle:rownum
        2. 需要编写结果映射。
2. Hibernate技术特点:  
    * 好处:
        1. 标准的orm框架,程序员不需要编写SQL语句。
        2. 具有良好的数据库无关性,即数据库发生变化的话,代码无需再次编写。
            a)	以后,mysql数据迁移到oracle,只需要改方言配置
    * 缺点:
        1. 学习门槛高,需要对数据关系模型有良好的基础,而且在设置OR映射的时候,需要考虑好性能和对象模型的权衡。
        2. 程序员不能自主的去进行SQL性能优化。
3. 应用场景 
    1. MyBatis应用场景
        * 需求多变的互联网项目,例如电商项目。
    2. Hibaenate应用场景
        * 需求明确、业务固定的项目,例如OA项目、ERP项目等。

今日知识

1. 关联查询
2. 延时加载
3. 查询缓存

关联查询

1.一对一 resultType实现

1. 写个定单的扩展类
    public class OrdersExt extends Orders {
    private String username;
    private String address;
2. 声明定单接口
    public interface OrdersMapper {
    //根据订单id查询订单信息
    public OrdersExt findOrdersUser(int id);
}
3. 声明定单配置文件
    <mapper namespace="com.rqy.mapper.OrdersMapper">
    <sql id="select_orders">
        orders.id,
        orders.number,
        orders.createtime,
        orders.note,
    </sql>
    <sql id="select_user">
        user.username,
        user.address
    </sql>
    <select id="findOrdersUser" parameterType="int" resultType="ordersExt">
        SELECT
        <include refid="select_orders"/>
        <include refid="select_user"/>
        FROM
        orders,user
        WHERE
        orders.user_id=user.id and orders.id=#{?};
    </select>
    </mapper>
4. 加载映射文件
    <mappers>
        <package name="com.rqy.mapper"/>
    </mappers>
5. 测试
    @Test
    public void test(){
        OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
        OrdersExt ordersExt = ordersMapper.findOrdersUser(3);
        System.out.println(ordersExt);
        sqlSession.commit();

    }
6. 一对一 resultMap实现
     <!--一对一 resultMap实现-->
    <resultMap id="ordersRsMap" type="orders">
        <id column="id" property="id"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        <!--关联内部对象,Orders类有User user-->
        <association property="user" javaType="com.rqy.model.User">
            <id property="id" column="id"/>
            <result column="username" property="username"/>
            <result column="address" property="address"/>
        </association>
    </resultMap>
    <select id="findOrdersByRsMap" parameterType="int" resultMap="ordersRsMap">
        SELECT
        <include refid="select_orders"/>
        <include refid="select_user"/>
        FROM
        orders,user
        WHERE
        orders.user_id=user.id and orders.id=#{?};
    </select>

2.一对多 resultType实现

3.多对多 resultType实现

延时加载

  1. 概念:延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。
  2. 在mybatis中,resultMap标签的association标签和collection标签具有延迟加载的功能。
  3. 案例
posted @ 2019-08-04 22:22  励志前行  阅读(155)  评论(0编辑  收藏  举报