mybatis第二阶段

Mybatis第二阶段

输入映射跟输出映射

  • 输入映射(dao传入参数类型)

    1. 简单类型
      • #{}占位符:传入参数是简单类型(String,double,integer),#{}里面的名称随意写
      • ${}拼接符:传入的参数是简单类型(String,double,integer),${}里面必须是value
    2. pojo
      • #{}:传入参数是pojo类,#{}变量类型必须是pojo类中的属性.属性.属性。。。
      • ${}:传入参数是pojo类,#{}变量类型必须是pojo类中的属性.属性.属性。。。
  • 输出映射(dao的返回值类型)

    1. 简单类型:只有返回值为一行一列的时候,返回的才可以使简单类型
    2. pojo类型:
      • 使用resultType属性进行自动映射,查询的sql语句返回的列名必须和pojo的属性名称一一对应
      • 使用resultMap手动映射将列名跟pojo类中的属性一一映射
    3. List:使用resultType属性进行自动映射,要写list中的泛型的类型

使用resultMap的时候首先先要定义:

  • resultMap的使用
    • id属性:resultMap的唯一标识
    • type属性:返回的结果集标识
    • id标签:指定主键列的映射关系
      • column列:数据库中的列名
      • property:pojo类中的属性名称
    • result标签:指定非主键列的映射关系
      • column列:数据库中的列名
      • property:pojo类中的属性名称
    • association标签:指定单个对象的映射关系
      • 里面也是id跟result
  • 然后在使用的时候在,sql语句的标签上直接写resultMap=上面resultMap的id即可

动态sql

  • 作用:在一些高级查询的时候,sql语句中的where条件可能多也可能少,所以这时候就要动态的拼接sql

  • 在要写sql语句的后边可以添加:

    • where标签作用:
      1. 可以自动添加where关键字
      2. 可以去掉第一个条件中的and关键字
    • where里边也有if标签:判断传入的参数是否为空等判断条件用
  • 全局sql语句
    • sql标签:封住sql语句,封装后别的sql语句中可以重用这个条件.
    • include标签:引用封装好的sql语句,例如<include refide="userWhere"></include>.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- sql标签的作用是封装sql语句可以重用 -->
<sql id="userWhere">
<!-- where标签作用: 1. 可以自动添加where关键字, 2. 可以去掉第一个条件中的and关键字 -->
<where>
<if test="username != null and username != ''">
and username like '%${username}%'
</if>
<if test="sex != null and sex != ''">
and sex =#{sex}
</if>
</where>
</sql>
<select id="findUserByNameAndSex" parameterType="cn.itcast.pojo.User" resultType="cn.itcast.pojo.User">
select * from user
<!-- 引用封装好的sql语句 -->
<include refid="userWhere"></include>
</select>
  • where中的foreach标签,拼接功能:遍历传入的集合数据
    • collection: 要遍历的集合变量
    • item: 每遍历一次当前的变量数据
    • open:循环开始拼接的字符串
    • close:循环结束拼接的字符串
    • separator:分隔符
1
2
3
4
5
6
7
8
9
10
<select id="findUserByIds" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.User">
select * from user
<select id="findUserByIds" parameterType="cn.itcast.pojo.QueryVo" resultType="cn.itcast.pojo.User">
select * from user
<where>
<foreach collection="ids" item="id" open="id in(" close=")" separator=",">
#{id}
</foreach>
</where>
</select>

关联映射

一对一

以订单表跟用户表为例,订单跟用户从整体上来看是多对一,但是从局部上来看是一对一

  1. 使用自动映射resultType,可以封装大而全的自定义pojo把要接受的数据的属性全都封装在其中
1
2
3
4
5
6
7
8
9
10
11
12
13
//集成订单对象, 拥有订单对象中所有属性和方法, 又加入了user的属性
public class CustomOrdersAndUser extends Orders{
private Integer uid;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
//映射文件
<select id="findOrdersAndUser1" resultType="cn.itcast.pojo.CustomOrdersAndUser">
select o.*, u.id uid, u.username, u.birthday,u.sex,u.address
from orders o, user u where o.user_id = u.id
</select>
  1. 使用手动映射resultMap,需要手动指定sql的列名和pojo中的属性名的关系,在resultMap中可以使用association标签来映射单个对象的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private User user;
public class User {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private List<Orders> ordersList;
//映射文件
<resultMap type="cn.itcast.pojo.Orders" id="ordersAndUserResultMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<!-- association标签: 指定对应单个对象的映射关系
property: 将查询出的用户的数据放入Orders对象中的user属性中保存
javaType: property中指定的user属性的类型
-->
<association property="user" javaType="cn.itcast.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 一对一手动映射为 -->
<select id="findOrdersAndUser2" resultMap="ordersAndUserResultMap">
select o.*, u.id uid, u.username, u.birthday,u.sex,u.address
from orders o, user u where o.user_id = u.id
</select>

多对多

自动映射是用不了的,所以使用手动映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<resultMap type="cn.itcast.pojo.User" id="userAndOrdersResultMap">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!--
collection标签指定对集合对象关系映射
property:将查询出的订单数据放入User对象中的ordersList属性中保存
ofType: ordersList的泛型的类型
-->
<collection property="ordersList" ofType="cn.itcast.pojo.Orders">
<id column="oid" property="id"/>
<result column="user_id" property="userId"/>
<result column="createtime" property="createtime"/>
<result column="number" property="number"/>
</collection>
</resultMap>
<select id="findUserAndOrders" resultMap="userAndOrdersResultMap">
select u.*,o.id oid, o.user_id, o.createtime, o.number
from user u, orders o where u.id = o.user_id
</select>

mybatis跟spring的整合

  • 映射文件, 接口, mybatis核心配置文件
  • Application-Context.xml
    • spring的配置文件, 这里面配置了数据源, 连接池, 会话工厂, 引入原生dao或者是动态代理的接口

使用

  1. 导入jar包:mybatis,spring,两者的整合包,还有log4j包,数据库连接的包…

  2. 创建appliationContext.xml配置文件

    1. 加载jdbc配置文件
    2. 配置数据库连接池
    3. 配置会话工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 加载配置文件 -->
<context:property-placeholder location="jdbc.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>
<!-- 配置会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置mybatis核心配置文件的位置 -->
<property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
<!-- 引用上面配置好的数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>

测试原生DAO开发

  • 在spring的配置文件中配置UserDao,把会话工厂给配置进去
1
2
3
<bean id="userDao" class="cn.itcast.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
  • 接口
1
2
3
4
public interface UserDao {
public User findUserbyId(Integer id);
public List<User> findUserByName(String userName);
}
  • 实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Override
public User findUserbyId(Integer id) {
//会话是线程不安全的, 所以它的最佳使用范围在方法体内
SqlSession openSession = this.getSqlSession();
User user = openSession.selectOne("test.findUserById", id);
return user;
}
@Override
public List<User> findUserByName(String userName) {
//会话是线程不安全的, 所以它的最佳使用范围在方法体内
SqlSession openSession = this.getSqlSession();
List<User> list = openSession.selectList("test.findUserByUserName", userName);
return list;
}
}

动态代理的方式

引入单个Mapper(不常用)

1
2
3
4
5
6
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 配置mapper接口的全路径名称呢 -->
<property name="mapperInterface" value="cn.itcast.mapper.UserMapper"></property>
<!-- 引入会话工厂 -->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

但是这种方法有一个局限性,就是每一个dao都要配置一下,代码量太大了,所以一般用下面这种方法

使用包扫描的方式批量引入整个包下的mapper

  • 推荐使用包扫描的时候不用引入会话工厂, 它会自动调用当前配置文件下的会话工厂配置
  • 实例化引用的时候, 使用类名, 首字母小写
1
2
3
4
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 引入包的全路径名称 -->
<property name="basePackage" value="cn.itcast.mapper"></property>
</bean>

逆向工程

  • 作用:替我们连接数据库根据表生成pojo,接口和映射文件

  • 导入三个包:

    • 还有数据库连接包
  • mapper生成配置文件

    • generatorConfig.xml中配置mapper生成的详细信息,注意改下几点:
      • 添加要生成的数据库表
      • 文件所在包路径
      • mapper文件所在包路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?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="admin">
</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.itcast.ssm.po"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="cn.itcast.ssm.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="cn.itcast.ssm.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="items"></table>
<table tableName="orders"></table>
<table tableName="orderdetail"></table>
<table tableName="user"></table>
<!-- <table schema="" tableName="sys_user"></table>
<table schema="" tableName="sys_role"></table>
<table schema="" tableName="sys_permission"></table>
<table schema="" tableName="sys_user_role"></table>
<table schema="" tableName="sys_role_permission"></table> -->
<!-- 有些表的字段需要指定java类型
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table> -->
</context>
</generatorConfiguration>
  • 使用java类生成mapper文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
Public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
  • 运行,拷贝生成的mapper文件到工程中指定的目录中
    • Mapper.xml的文件拷贝至mapper目录内
    • Mapper.java的文件拷贝至mapper目录内
    • 注意:mapper xml文件和mapper.java文件在一个目录内且文件名相同。

逆向工程注意事项

  • Mapper文件内容不覆盖而是追加
    • Mapper.xml文件已经存在时,如果进行重新生成则mapper.xml文件内容不被覆盖而是进行内容追加,结果导致mybatis解析失败。
  • 解决方法:删除原来已经生成的mapper xml文件再进行生成。
  • Mybatis自动生成的po及mapper.java文件不是内容而是直接覆盖没有此问题

Table schema问题

下边是关于针对oracle数据库表生成代码的schema问题:

  • Schma即数据库模式,oracle中一个用户对应一个schema,可以理解为用户就是schema
  • 当Oralce数据库存在多个schema可以访问相同的表名时,使用mybatis生成该表的mapper.xml将会出现mapper.xml内容重复的问题,结果导致mybatis解析错误。
  • 解决方法:

    • 在table中填写schema,如下:<table schema="XXXX" tableName=" " >
    • XXXX即为一个schema的名称,生成后将mapper.xmlschema前缀批量去掉,如果不去掉当oracle用户变更了sql语句将查询失败。
    • 快捷操作方式:mapper.xml文件中批量替换:“from XXXX.”为空
  • Oracle查询对象的schema可从dba_objects中查询,如下:
    select * from dba_objects

张冲 wechat
欢迎扫一扫上面的微信关注我,一起交流!
坚持原创技术分享,您的支持将鼓励我继续创,点击打赏!