2020:0603--mybatis(二)

271 阅读5分钟

主要内容

1.回顾mybatis的自定义再分析和环境搭建+完善基于注解的mybatis
2.mybatis的curd(基于代理dao的方式)  ----  实际中一般用dao的代理模式
3.mybatis中的参数深入及结果集的深入
4.mybatis中基于传统dao的方式(编写dao的实现类)----- 用的不多了解
5.mybatis中的配置(主配置文件:SqlMapConfig.xml)
        properties标签
        typeAliases标签
        mappers标签

2. mybatis的curd操作(基于代理dao的方式)

2.1 保存(增加)操作:saveUser
2.1.1 代码实现:saveUser
```
* IUserDao:持久层接口:
    public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();

* IUserDao.xml:持久层映射配置文件
    <!--
        1.mybatis框架可以通过传入的这个参数类型:parameterType=User,
          然后反射出来里面的属性getXxx()中的Xxx,拿到值
        2.配置插入操作后,获取刚才插入数据的id
          keyProperty="id"   对应实体类的id的属性名称
          keyColumn="id"      id的列名,对应表
          order="AFTER"       什么时候执行获取id的操作,在执行插入之后
    -->

    <insert id="saveUser" parameterType="User">
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select 1676298;
        </selectKey>
        insert into user(username, address, sex, birthday)values (#{username},#{address},#{sex},#{birthday});
    </insert>

* MybatisTest:测试类
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //3.获取SqlSession对象
    sqlSession = factory.openSession();
    //4.获取dao的代理对象
    userDao = sqlSession.getMapper(IUserDao.class);


    User user = new User();
    user.setUsername("mybatis saveuser");
    user.setAddress("北京市顺义区");
    user.setSex("男");  //数据库中是一个字符
    user.setBirthday(new Date());

    userDao.saveUser(user);
    
	/**
	注意:sqlSession.commit();
		使用Mybatis添加用户时,测试显示成功,但数据库中没有添加的数据。
		控制台输出:
    		Setting autocommit to false on JDBC Connection
    		Rolling back JDBC Connection
		意思是Sqlsession默认把自动事务提交给关闭了,开启了手动事务提交,
		因为没有自动提交上去,所以将事务进行了回滚
		只有把事务提交,数据才能提交到数据库。
		
		加上sqlSession.commit();  提交事务,数据就保存上去了
		数据库表中的结果:id=49 之后 直接到了id=52
		因为之前的几次test事务都没有提交,虽然主键也分配了,但是事务没有提交成功,导致事务又回滚了。
	
	*/
    sqlSession.commit();
	
    sqlSession.close();
    in.close();
```
2.1.2 OGNL表达式
    * Object Graphic Navigation LanguageCallback
      对象	  图	  导航		语言
    
    * 它是通过对象的取值方法来获取数据,在写法上把get给省略了。
    * 比如:我们获取用户的名称:
        类中的写法:user.getUsername();
        OGNL表达式的写法:user.username
    * IUserDao.xml中为什么能直接写username,而不用user.username呢:
        因为在parameterType属性中已经提供了属性所属的类,所以此时不需要写对象名属性名
        ```
       <update id="updateUser" parameterType="com.itheima.domain.User">
            update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
        </update>
        
        //username=#{username}  ----  >  username=user.username  ---- > username=user.getUsername
        ````
2.2 传递pojo包装对象:将查询条件包装成一个实体类 public class QueryVo
2.2.1 代码实现
    * 开发中通过pojo传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件
(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

    * 需求:根据用户名和用户性别查询用户信息,查询条件放到QueryVo的user属性中。
    
        ```
        * 查询条件对象 QueryVo
        public class QueryVo {
        
            private User user;
        
            public User getUser(){
                return user;
            }
        
            public void setUser(User user){
                this.user = user;
            }
        }
        
        * IUserDao:持久层接口:
        List<User> findUserByVo(QueryVo vo);
        
        * IUserDao.xml:持久层映射配置文件
        <!--根据queryVo的条件查询用户-->
        <select id="findUserByVo" parameterType="com.itheima.domain.QueryVo" resultType="com.itheima.domain.User">
            select * from user where username like #{user.username} AND sex like #{user.sex}
        </select>
        注意1:这里的parameterType="com.itheima.domain.QueryVo"
               所以#{user.username}:user是QueryVo中的user,再用OGNL表达式user.username得到username
              
        * MybatisTest:测试类:
        @Test
        public void testFindUserByVo(){
            QueryVo vo = new QueryVo();
            User user = new User();
            user.setUsername("%王%");
            user.setSex("男");
            vo.setUser(user);
    
            List<User> users = userDao.findUserByVo(vo);
            for (User user1 : users) {
                System.out.println(user1);
            }
        }
        注意2:
        ```
2.3 测试模糊查询FindByName()
2.3.1 代码实现
    ```
    * IUserDao:持久层接口:
    List<User> findByName(String userName);
    
    * IUserDao.xml:持久层映射配置文件
    <select id="findByName" parameterType="String" resultType="com.itheima.domain.User">
        <!--select * from user where username like '%${value}%'--!>
        select * from user where username like #{name};
    </select>
    
    * MybatisTest:测试类:
    @Test
    public void testFindByName(){
        //List<User> users = userDao.findByName("王");
        List<User> users = users = userDao.findByName("%王%");
        for (User user : users) {
            System.out.println(user);
        }
    }
    ```
2.3.2 注意:占位符和sql拼接 : #{}和${}
    * IUserDao.xml:持久层映射配置文件
    写法1:select * from user where username like #{name};
    写法2:select * from user where username like '%${value}%';
    写法2的话,只能是固定写法${value},因为源码中指定了读取的key的名字就是“value”,所以我们在绑定参数时,只能叫做value
        
        
    * 几种情况
    情况1:'%${value}%' sql语句拼接
        IUserDao.xml:select * from user where username like '%${value}%'
        testFindByName():userDao.findByName("王");
        最终的得到的SQL:
        * select * from user where username like '%王%';
        Preparing: select * from user where username like '%王%';
        Parameters: (什么都没)
        这个用的是Statement对象的字符串的拼接来实现,直接将参数拼上去
    情况2:#{name}; 占位符
        IUserDao.xml:select * from user where username like #{name};
        testFindByName():users = userDao.findByName("%王%");
        最终的得到的SQL:
        * select * from user where username like ?;
        Preparing: select * from user where username like ?;
        Parameters: %王%(String)
        这个用的是PrepareStatement参数占位符-------显然带有预处理的方式要更好,实际开发用的更多。
    情况2:这是错的
        IUserDao.xml:select * from user where username like '%${value}%'
        testFindByName():users = userDao.findByName("%王%");
        最终的得到的SQL:
        * select * from user where username like '%%王%%';
2.4 测试更新操作 updateUser
2.4.1 代码实现
    ```
    * IUserDao:持久层接口:
    void updateUser(User user);
    
    * IUserDao.xml:持久层映射配置文件
    <update id="updateUser" parameterType="user">
        update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
    </update>
    
    * MybatisTest:测试类:
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(61);
        user.setUsername("mybatis Update user");
        user.setAddress("北京市顺义区");
        user.setSex("女");  //数据库中是一个字符
        user.setBirthday(new Date());

        //执行更新方法
        userDao.updateUser(user);
    }
    ```
2.5 测试删除操作 deleteUser(通过用户id删除)
    ```
    * IUserDao:持久层接口
    void deleteUser(Integer userId);
    
    * IUserDao.xml:持久层映射配置文件
    <!--
        parameterType:int/INT/Integer/INTEGER/java.lang.Integer都可以,为什么支持这么多写法呢?
        因为mybatis中使用了别名,我们也可以在主配置文件中用<typeAliases>标签配置别名
        而且别名不区分大小写

        思考:当参数传递是一个基本类型/基本类型的包装类时,且参数只有一个时,这个参数形式可以随意写:uid/id/user_id...
        parameterType 参数的类型要和IUserDao中的参数类型对应上。
    -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id = #{uid};
    </delete>
    
    * MybatisTest:测试类:
    @Test
    public void testDelete(){
        userDao.deleteUser(53);
    }
    ```
2.6 测试根据ID查询一个 findById
    ```
    * IUserDao:持久层接口
    User findById(Integer userId);
    
    * IUserDao.xml:持久层映射配置文件
    <select id="findById" parameterType="INT" resultType="com.itheima.domain.User">
        select * from user where id = #{id};
    </select>
    
    * MybatisTest:测试类:
   @Test
    public void testFindOne(){
        //执行查询一个方法
        User user = userDao.findById(48);
        System.out.println(user);
    } 
    ```
2.7 Mybatis的输出结果封装
2.7.1 实体类中的属性名称必须和查询语句中的列名保持一致
    windows mysql数据库不区分大小写
    linux中的mysql是严格区分大小写


    实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。
    当我们使用查询的结果进行封装时,要求我们的实体类必须和数据库的类名保持一致。 
    
    ```
	* 实体类:User 
	public class User implements Serializable {
		private Integer UserId;
		private String userName;
		private String userAddress;
		private String userSex;
		private Date useBirthday;
	}
	* MySql:
		id
		username
		birthday
		sex
		address
    ```
2.7.2 解决方法1:使用别名
    * 实体类:User 
    public class User implements Serializable { 
        private Integer userId; 
        private String userName; 
        private Date userBirthday; 
        private String userSex; 
        private String userAddress;
    }
    * MySql:
    	id
    	username
    	birthday
    	sex
    	address
		
    * IUserDao.xml
	<insert id="saveUser" parameterType="User">
	    insert into user(username, address, sex, birthday) values (#{useNname},#{userAddresss},#{userSex},#{useBirthday});
	</insert>
	
	这样是无法将结果集进行封装的,不满足实体类中的属性名称和查询语句中的列名保持一致
	
* 解决方法:使用别名
	```
	<select id="findAll" resultType="com.itheima.domain.User">
		select id as userID,username as userName,address as userAddress,sex as userSex, 
		    birthday as useBirthday from user;
	</select>	
	这种解决方式的执行效率是最高的,因为是在sql语句层面就解决了。
	```
2.7.3 解决方法2:配置
        <!--配置 查询结果的列名和实体类的属性名的对应关系-->
	<!--property:是java中的属性,colum:是数据库的列名-->
	<resultMap id="userMap" type="com.itheima.domain.User">
		<!--主键段字段的对应-->
		<id property="userId" colum="id"></id>
		<!--非主键段字段的对应-->
		<result property="userNmae" colum="username"></result>
		<result property="userAddress" colum="address"></result>
		<result property="userSex" colum="sex"></result>
		<result property="userBirthday" colum="birthday"></result>
	</resultMap> 
	
	<!--
		注意:要加上resultMap属性,resultType不需要了
		结果就可以封装了
	-->
	<select id="findAll" resultMap="userMap">
		select * from user;
	</select>
	这种解决方法要多解析一点xml,执行效率变慢,但是开发效率快了。
	其他的查询操作,也可以加上resultMap="userMap"属性。

3. 传统dao的方式(编写dao的实现类)--- 用的不多

3.1 写dao实现类_mybatis执行过程分析

3.2 Dao代理对象_mybatis执行过程

4.mybatis中的配置(主配置文件:SqlMapConfig.xml)

4.1 properties标签
* 配置properties
    可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
    1.resource属性:
        用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下
    2.url属性:
        是要求按照url的写法来写配置文件的地址
        URL:Uniform Resource Locator,统一资源定位符,可以唯一定位一个资源位置
        写法:
            http://localhost:8080/baidu.com
            协议    主机      端口   URI
        * URI: Uniform Resource Identifier 统一资源标识符,可以在应用(项目)中唯一定位一个资源。

        * Windows文件:用的是file协议 file:///

```
    * jdbcConfig.properties
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/txl_mybatis
    jdbc.username=root
    jdbc.password=root
```

```
<configuration>
    <properties url="file:///E:/Idea_Projects/day0603_01mybatisCRUD/src/main/resources/jdbcConfig.properties">
        <!--<property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/txl_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>-->
    </properties>

    <!--使用typeAliases配置别名
        只能配置domain中类的别名,从而让我们在用的时候少写一些包名或全限定类名
    -->
    <typeAliases>
        <!--typeAlias用于配置别名,type属性指定实体类的全限定类名,alias属性指定别名。当指定了别名后就不区分大小写-->
        <!--<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>-->
        <!--用于指定要指定别名的包,当指定后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
        <package name="com.itheima.domain"/>
    </typeAliases>

    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池 :下面的property可以引用上面的property
                            jdbc.是jdbcConfig.properties中key的前缀
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射文件的配置-->
    <mappers>
        <!--<mapper resource="com/itheima/Dao/IUserDao.xml"></mapper>-->
        <!--package标签是用于指定dao接口所在的包,当指定完成之后,就不需要再写:
            <mapper resource="com/itheima/Dao/IUserDao.xml"></mapper>或
            <mapper class="com/itheima/Dao/IUserDao"></mapper>
        -->
        <package name="com.itheima.Dao"/>
    </mappers>
</configuration>
    ```
4.2 typeAliases标签配置别名
    1.typeAlias用于配置别名,type属性指定实体类的全限定类名,alias属性指定别名。当指定了别名后就不区分大小写
        <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
        ```
            <configuration>
                <typeAliases>
                    <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
                </typeAliases>
            </configuration> 
        ```
    2.package用于指定要指定别名的包,当指定后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写>
        <package name="com.itheima.domain"/>
        ```
            <configuration>
                <typeAliases>
                    <package name="com.itheima.domain"/>
                <package name="com.itheima.domain"/>
            </configuration>
        ```


##### 4.3 mappers标签配置别名

    package标签是用于指定dao接口所在的包,当指定完成之后,就不需要再写:
            <mapper resource="com/itheima/Dao/IUserDao.xml"></mapper>或
            <mapper class="com/itheima/Dao/IUserDao"></mapper>
    
    <configuration>
        <mappers>
            <!--<mapper resource="com/itheima/Dao/IUserDao.xml"></mapper>-->
            <package name="com.itheima.Dao"/>
        </mappers>
    </configuration>