Mybatis学习笔记二:代理接口xml文件配置,关联查询,逆向工程,spring整合Mybatis框架

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33322074/article/details/89483562

1. Mybatis学习笔记二:

1.1. 序列化和反序列化

  1. 为什么要在类里面添加序列化呢?
    • 当数据从一台电脑通过公网传递到另一台电脑时就要序列化,因为要将数据生成二进制文件,所以要序列化,然后另一台电脑接收,接收的是二进制文件,只能反序列化。
public static final long serialVersionUID=1L;

1.2. ParameterType(输入参数类型)

  1. 传递简单类型,Integer ,String 等类型
    • Mapper.xml用#{},和${}接收参数。两个表达式的区别是:前者是占位符,后者是字符串拼接。
  2. 传递pojo对象,
    • Mapper.xml 使用ongl表达式的#{}或者${}获取参数值。
    • parameterType类型值是pojo类,就可以用#{}获取该类里的属性值。
    • java代码先将参数封装到pojo对象中,然后sql语句再根据ParameterType从中获取。
<update id="updateUserById" parameterType="com.it.mybatis.pojo.User">
    update user 
    set username = #{username},sex = #{sex},birthday = #{birthday},address = #{address}
    where id = #{id}
</update>
  1. 传递pojo包装对象
    • 为什么要传递包装类参数?因为我们的查询条件可能有一种或多种,我们可以将对象作为属性封装到一个类里就可以实现多条件查询了。然后将要查询的内容封装到属性中。
    • 我们这样做的目的就是将传递的多个参数封装到属性对象中,它不像添加用户,添加用户会将添加的参数保存进去。我们重新创建了一个pojo类,这个类里面包含某个对象属性,相当于借用了这个容器,就像servlet技术中的web层,也是借用了pojo的容器,存储到栈顶了。然后在Mapper.xml中取出作为多条件来查询。
    • 在Mapper.xml中我们接收的参数类型是这个封装的类,
public class QueryVo {
    // 包含其他的pojo
    private User user;
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}
  • 上面是创建新的容器,里面包含User属性对象。
<!-- 根据用户名模糊查询 -->
<select id="findUserByQueryVo" parameterType="QueryVo" resultType="com.it.mybatis.pojo.User">
    select * from user where username like "%"#{user.username}"%"
</select>
  • 上面采用了模糊查询,我们还可以用and关键字多增加几个查询条件。
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("五");
vo.setUser(user);
List<User> us = userMapper.findUserByQueryVo(vo);
for (User u : us) {
    System.out.println(u);
}
  • 上面将查询的内容封装到User属性对象里。在Mapper.xml中可以取出该对象的值。
1.2.1. 思考
  1. 为什么要添加一个包装类,直接用User对象不行吗?
    • 重新创建一个包装类,将原来的实体类作为属性对象,我们可能查询的不是一个表,那么就要用到包装类了,因为包装类可以包含多个实体对象,但是传递的参数还是一个包装类对象。岂不美哉。

1.3. resultMap手动映射

  1. 原因分析
    • 这个主要是针对数据库表字段和实体类型属性是否一致的,如果一致,就不用设置了,如果不一致必须进行设置,设置内容和Hibernate的设置差不多。
    • resultMap的值对应resultMap中的id值。
    • resultMap中type对应的值为实体类
    • column对用表中的字段,property对应实体类的属性。
    • 如果表中的字段和实体类的属性名字相同可以不必写。Mybatis默认自动配置。
<resultMap type="Orders" id="orders">
    <result column="user_id" property="userId"/>
</resultMap>
<select id="selectOrdersList" resultMap="orders">
    SELECT id, user_id, number, createtime, note FROM orders 
</select>

1.4. 动态sql

  1. 通过mybatis提供的各种标签方法实现动态拼接sql。
1.4.1. if标签
  1. if标签
    • 用在Mybatis的条件查询语句当中。
 select * from user
<if test="sex != null and sex != ''">
    and sex = #{sex} 
</if>
<if test="username != null and username != ''">
    and username = #{username}
</if>
</where>
  1. where标签
    • 我们在使用条件查询语句时,习惯设置一个where 1=1;用于防止查询条件只有where没有判断值。而Mybatis提供了where标签,可以不用再使用where 1=1 这个判断。
    • where 使用时,第一个判断的And可以省略不写。And加在判断的前面。
    • 有了where标签,sql语句中的where还可以不写。
<!--	根据性别和名字查询用户  where 可以去掉第一个前ANd   -->
<select id="selectUserBySexAndUsername" parameterType="User" resultType="User">
    select * from user
    <where>
        <if test="sex != null and sex != ''">
            and sex = #{sex} 
        </if>
        <if test="username != null and username != ''">
            and username = #{username}
        </if>
    </where>
</select>
  1. sql标签
    • sql片段可以提取出来,为什么要提取sql片段,比如上面的那段代码,里面的select * from user会大量重复,Mybatis提供了这样的机制,可以用sql标签实现提取重复的sql语句。
    • 先用sql标签定义,然后再用include引用。
    • refid就是依赖的意思需要和sql标签中的id一致
<sql id="selector">
    select * from user
</sql>
<include refid="selector"/>
  1. foreach标签

    • 向sql传递数组或List,Mybatis提供了foreach标签用于解析。传来的list集合和数组参数。

    • 将多个参数组合成数组,或者集合然后传到Mapper.xml,Mapper.xml使用foreach进行解析。

    • <select 中id值为接口中的方法名。

    • collection的值为包装类中的属性对象。

    • 如果传来的参数是List集合,collection值就是list,如果是数组值就是array,如果是包装类,就用包装类里面的属性对象

    • item的值为随意的一个变量,与for增强循环遍历类似。

    • separator:用什么分隔开来。指定分隔符

    • open:这个是用来增加括弧的,即左括弧。里面加入了id in表示id in在左括弧左边。

    • close是右括弧。

    • 下面整句话的意思是:select * from user where id in(?,?,?);

<!-- 多个ID (1,2,3)-->
<select id="selectUserByIds" parameterType="QueryVo" resultType="User">
    <include refid="selector"/>
    <where>
        <foreach collection="list" item="id" separator="," open="id in (" close=")">
            #{id}
        </foreach>
    </where>
</select>

2. 关联查询

2.1. 一对多关联

  1. 一对一关联即:一个表与另一个表有关联关系,也就是说一个表里有另一个表的外键,我们做查询的时候可以通过外键将两个表关联起来,生成一个新的表。这个新的表带有上面两个表的字段。
  2. 一对一关联的创建步骤:
    • 改造pojo类。
    • 配置Mapper.xml
    • 创建Mapper接口
    • 书写测试类

2.2. 一对多关联实现

  1. 改造pojo类
    • 有两种方式:一是在原有实体类的基础上创建一个新的实体类,这个新的实体类继承原先的实体类,这样继承的实体类相当于增加了属性。
    • 另一种是直接在原先的实体类中添加属性对象,并生成set()和get()方法。我更喜欢第二种方法。
    • 多的实体类添加了属性对象。那个实体类就对应有外键的表。
    • 一的实体类添加集合,表示该实体一个对象可以有多个其他的表记录
  2. 配置Mapper.xml
    • resultMap标签中的type值为对应接口方法中返回结果类型而定,如果是泛型,就要看泛型类型,id是用于select标签的标识。
    • resultMap标签内id,result分别对应表的主键和实体类的对应,result对应表的普通字段和实体类中的对应。
    • resultMap中的collection标签对应的是返回结果类型对应实体类中的新增属性。如果是集合类型就是conllection,
    • collection中的property属性值:是返回对象实体类中的新增属性对象。
    • ofType的值为一方集合中的泛型对象类型,而不是集合类型。
//一对多关联
public List<User> selectUserList();
  • 返回结果集类型的泛型是User
<resultMap type="User" id="user">
    <id column="user_id" property="id"/>
    <result column="username" property="username"/>
    <!-- 一对多 -->
    <collection property="ordersList" ofType="Orders">
        <id column="id" property="id"/>
        <result column="number" property="number"/>
    </collection>
</resultMap>
<select id="selectUserList" resultMap="user">
    SELECT 
    o.id,
    o.user_id, 
    o.number,
    o.createtime,
    u.username 
    FROM user u
    left join orders o 
    on o.user_id = u.id
</select>

2.3. 一对一关联配置

  1. 一对一配置很少见,意义不大,几乎没有。一般只有一对多,和多对多。
    • resultMap即返回结果映射类型,要根据接口中方法的返回类型中的泛型决定。
    • 多对多采用conllection,而一对一采用的是assocation标签
    • 剩下的原理和一对多相似。
<!-- 
  //一对一关联 查询  以订单为中心 关联用户
 public List<Orders> selectOrders();
  -->
<resultMap type="Orders" id="order">
    <result column="id" property="id"/>
    <result column="user_id" property="userId"/>
    <result column="number" property="number"/>
    <!-- 一对一 -->
    <association property="user" javaType="User">
        <id column="user_id" property="id"/>
        <result column="username" property="username"/>
    </association>
</resultMap>
<select id="selectOrders" resultMap="order">
    SELECT 
    o.id,
    o.user_id, 
    o.number,
    o.createtime,
    u.username 
    FROM orders o 
    left join user u 
    on o.user_id = u.id
</select>

2.4. 测试代码

@Test
public void testUserList() throws Exception {
    //加载核心配置文件
    String resource = "sqlMapConfig.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    //创建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    //创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //SqlSEssion帮我生成一个实现类  (给接口)
    OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
    List<User> users = orderMapper.selectUserList();
    for (User user : users) {
        System.out.println(user);
    }
}

3. Mybatis的逆向工程

3.1. 逆向工程用途

  1. Hibernate是根据实体类和映射文件生成数据库表,Mybatis是根据数据库表生成实体类和Mapper.xml文件,大大提高了开发效率。注意:逆向工程工具用来生成

3.2. 逆向工程的使用

  1. 下载逆向工程生成工具包:https://github.com/mybatis/generator/releases 这个主要是用来下载逆向工程的jar核心包的。至于生成代码的逆向工程工具,自己在网上找。
  2. 复制逆向工程到工作空间,eclipse导入逆向工程,与导入web工程一样的方法。
  3. 在generatorConfig.xml中配置逆向工程,主要配置三个方面:
    • 修改要生成的数据库表。
    • pojo文件所在的包路径
    • Mapper.java接口所在的包路径
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
	<context id="testTables" targetRuntime="MyBatis3">
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:否 -->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>
		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		<jdbcConnection driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="root">
		</jdbcConnection>
		<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
			userId="yycg" password="yycg"> </jdbcConnection> -->

		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 
			和 NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="cn.it.ssm.po"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
		<!-- targetProject:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="cn.it.ssm.mapper"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="cn.it.ssm.mapper" targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表 -->
		<table schema="" tableName="user"></table>
		<table schema="" tableName="order"></table>
	</context>
</generatorConfiguration>
  1. 修改生成的数据库表

配置逆向工程xml

  1. 指定生成包的路径

指定包路径

  1. 指定生成的数据库表

指定数据库表

  1. 表字段指定类型
    • 这个不常用,因为主要是针对数据库中类型tyint的,因为逆向工程会将其生成boolean类型,而实际上它可以包含-127-128个数。为了防止它生成boolean在javaType值设置为Integer.
<!-- 有些表的字段需要指定java类型-->
<table schema="" tableName="">
    <columnOverride column="" javaType="" />
</table> 

3.3. 启动逆向工程

  1. 运行GeneratorSqlmap.java,就会生成Mapper.xml,Mapper.java以及pojo的类。其中pojo包中里面包含了xxxExample类,这些类里包含了很多链接数据库的方法,都是单表查询的方法,可以通过调用这些方法执行sql。如果需要多表连接,就要重新写方法。
  2. 不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
  3. 一张表会生成4个文件

3.4. 逆向工程一些代码分析

  1. 这个两个方法的区别
    • selective表示更新表记录时,如果更新的是部分字段,那么selective就会更新部分字段。
    • 下面那个会将全部字段更新,如果没有写,就是null.
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);

4. spring整合Mybatis框架

4.1. 整合思路

  1. Mybatis的SqlSessionFactory对象应该整合到spring容器中作为单例存在。
  2. 传统的dao的开发方式中,应该从spring容器中获得sqlsession对象。
  3. Mapper代理形式中,应该从spring容器中获取mapper的代理对象。
  4. 数据库连接以及连接池事务管理都要交给spring容器来管理。

4.2. 准备工作

  1. 导包
    • 包含mybatis框架包和Spring框架包
  2. 创建工程,配置Mybatis的配置文件SqlMapConfig.xml
    • 添加Mybatis约束
    • 配置别名,指定扫描包
<configuration>
    <!-- 设置别名 -->
    <typeAliases>
        <!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
        <package name="com.it.mybatis.pojo" />
    </typeAliases>
    <mappers>
        <package name="com.it.mybatis.mapper"/>
    </mappers>
</configuration>
  1. 配置applicationContext.xml这是spring容器的配置文件
    • Mybatis整合到spring需要一个jar包,即:mybatis-spring.jar
    • 添加spring约束
    • 添加数据库连接池配置内容
    • 配置SqlSessionFactory,这个是获取session的工厂。
    • 配置Mybatis的核心文件SqlMapConfig.xml
    • 配置数据源
    • 最后配置dao的实现类由spring注入。实现spring管理。dao的实例化
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="10" />
    <property name="maxIdle" value="5" />
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 配置mybatis核心配置文件 -->
    <property name="configLocation" value="classpath:SqlMapConfig.xml" />
    <!-- 配置数据源 -->
    <property name="dataSource" ref="dataSource" />
</bean>
</beans>
  1. 书写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
  1. 添加日志文件log4j.properties
# 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

4.3. 使用传统的dao开发

  1. 创建pojo类

  2. 创建dao接口

  3. 创建dao实现类

    • 要继承SqlSessionDaoSupport

4.4. 使用Mapper代理形式开发dao

  1. 创建xxxMapper.java接口
    • 主要定义方法,方法名和xxxMapper.xml的id相同
  2. 配置xxxMpper.xml
    • 这里面配置的是数据库的书写,返回类型,传参类型,方法对应id。和前面讲的一样。
  3. 配置Mapper代理
    • 既然需要spring来参与进来,那么spring在这里的作用是用一个Bean将xxxMapper.java类和SqlSessionFactory放入这个bean中,这样在测试类中调用这个bean来获取Mapper.java借口,和session了。这样在写代码时只需要调用applicationContext.xml就可以了。
    • 注意这个MapperFactoryBean,这个mybatis-spring.jar已经提供了。
    • spring的原理就是直接或者间接的将sqlMapConfig.xml、log4j.properties、db.properties、xxxMapper.xml,数据库连接池、Session工厂控制。
<!-- Mapper代理的方式开发方式一,配置Mapper代理对象 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<!-- 配置Mapper接口 -->
	<property name="mapperInterface" value="cn.it.mybatis.mapper.UserMapper" />
	<!-- 配置sqlSessionFactory -->
	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
  1. 测试代码
    • 运行前初始化,获取applicationContext.xml
    • 测试时创建代理实现类,并实现方法。
    • 通过getBean的方法(MapperFactoryBean的id值)获取applicationContext.xml中的MapperFactoryBean
@Test
public void testMapper() throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    //UserMapper mapper = ac.getBean(UserMapper.class);
    UserMapper mapper = (UserMapper) ac.getBean("userMapper");
    User user = mapper.findUserById(10);
    System.out.println(user);
}

4.5. 动态代理加强

  1. 加强的目的

    • 如果不加强的话,每xxxMapper.java一个接口都要在applicationContext.xml中将其配置到MapperFactoryBean中,因为测试代码要通过id值获取这个Bean。
    • 那么你写一个接口就要重新配置一个Bean,如果接口有几百个就要写几百个Bean。这样肯定是不行的。
  2. 配置MapperScannerConfigurer 容器,

    • 这个容器可以自动扫描,我们只需要指定该接口在哪个包下面就行了。它会自动扫描。
    • 该容器还会扫描sqlSessionFactory,也就是受在applicationContext中这个也不用配了。
    • 主要要指定name为基础包basePackage
<!-- Mapper代理的方式开发方式二,扫描包方式配置代理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 配置Mapper接口 -->
    <property name="basePackage" value="cn.it.mybatis.mapper" />
</bean>
  1. 测试类
    • 先获取applicationContext.xml
    • 测试法时不需要再通过获取id值找MapperScannerConfiguer了。
private ApplicationContext context;
@Before
public void setUp() throws Exception {
    this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
  • 测试方法
@Test
public void testQueryUserById() {
    // 获取Mapper
    UserMapper userMapper = this.context.getBean(UserMapper.class);
    User user = userMapper.queryUserById(1);
    System.out.println(user);
}

猜你喜欢

转载自blog.csdn.net/qq_33322074/article/details/89483562