版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33322074/article/details/89483562
1. Mybatis学习笔记二:
1.1. 序列化和反序列化
- 为什么要在类里面添加序列化呢?
- 当数据从一台电脑通过公网传递到另一台电脑时就要序列化,因为要将数据生成二进制文件,所以要序列化,然后另一台电脑接收,接收的是二进制文件,只能反序列化。
public static final long serialVersionUID=1L;
1.2. ParameterType(输入参数类型)
- 传递简单类型,Integer ,String 等类型
- Mapper.xml用#{},和${}接收参数。两个表达式的区别是:前者是占位符,后者是字符串拼接。
- 传递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>
- 传递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. 思考
- 为什么要添加一个包装类,直接用User对象不行吗?
- 重新创建一个包装类,将原来的实体类作为属性对象,我们可能查询的不是一个表,那么就要用到包装类了,因为包装类可以包含多个实体对象,但是传递的参数还是一个包装类对象。岂不美哉。
1.3. resultMap手动映射
- 原因分析
- 这个主要是针对数据库表字段和实体类型属性是否一致的,如果一致,就不用设置了,如果不一致必须进行设置,设置内容和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
- 通过mybatis提供的各种标签方法实现动态拼接sql。
1.4.1. if标签
- 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>
- 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>
- sql标签
- sql片段可以提取出来,为什么要提取sql片段,比如上面的那段代码,里面的select * from user会大量重复,Mybatis提供了这样的机制,可以用sql标签实现提取重复的sql语句。
- 先用sql标签定义,然后再用include引用。
- refid就是依赖的意思需要和sql标签中的id一致
<sql id="selector">
select * from user
</sql>
<include refid="selector"/>
-
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. 一对多关联
- 一对一关联即:一个表与另一个表有关联关系,也就是说一个表里有另一个表的外键,我们做查询的时候可以通过外键将两个表关联起来,生成一个新的表。这个新的表带有上面两个表的字段。
- 一对一关联的创建步骤:
- 改造pojo类。
- 配置Mapper.xml
- 创建Mapper接口
- 书写测试类
2.2. 一对多关联实现
- 改造pojo类
- 有两种方式:一是在原有实体类的基础上创建一个新的实体类,这个新的实体类继承原先的实体类,这样继承的实体类相当于增加了属性。
- 另一种是直接在原先的实体类中添加属性对象,并生成set()和get()方法。我更喜欢第二种方法。
- 多的实体类添加了属性对象。那个实体类就对应有外键的表。
- 一的实体类添加集合,表示该实体一个对象可以有多个其他的表记录
- 配置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. 一对一关联配置
- 一对一配置很少见,意义不大,几乎没有。一般只有一对多,和多对多。
- 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. 逆向工程用途
- Hibernate是根据实体类和映射文件生成数据库表,Mybatis是根据数据库表生成实体类和Mapper.xml文件,大大提高了开发效率。注意:逆向工程工具用来生成
3.2. 逆向工程的使用
- 下载逆向工程生成工具包:https://github.com/mybatis/generator/releases 这个主要是用来下载逆向工程的jar核心包的。至于生成代码的逆向工程工具,自己在网上找。
- 复制逆向工程到工作空间,eclipse导入逆向工程,与导入web工程一样的方法。
- 在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>
- 修改生成的数据库表
- 指定生成包的路径
- 指定生成的数据库表
- 表字段指定类型
- 这个不常用,因为主要是针对数据库中类型tyint的,因为逆向工程会将其生成boolean类型,而实际上它可以包含-127-128个数。为了防止它生成boolean在javaType值设置为Integer.
<!-- 有些表的字段需要指定java类型-->
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table>
3.3. 启动逆向工程
- 运行GeneratorSqlmap.java,就会生成Mapper.xml,Mapper.java以及pojo的类。其中pojo包中里面包含了xxxExample类,这些类里包含了很多链接数据库的方法,都是单表查询的方法,可以通过调用这些方法执行sql。如果需要多表连接,就要重新写方法。
- 不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
- 一张表会生成4个文件
3.4. 逆向工程一些代码分析
- 这个两个方法的区别
- selective表示更新表记录时,如果更新的是部分字段,那么selective就会更新部分字段。
- 下面那个会将全部字段更新,如果没有写,就是null.
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
4. spring整合Mybatis框架
4.1. 整合思路
- Mybatis的SqlSessionFactory对象应该整合到spring容器中作为单例存在。
- 传统的dao的开发方式中,应该从spring容器中获得sqlsession对象。
- Mapper代理形式中,应该从spring容器中获取mapper的代理对象。
- 数据库连接以及连接池事务管理都要交给spring容器来管理。
4.2. 准备工作
- 导包
- 包含mybatis框架包和Spring框架包
- 创建工程,配置Mybatis的配置文件SqlMapConfig.xml
- 添加Mybatis约束
- 配置别名,指定扫描包
<configuration>
<!-- 设置别名 -->
<typeAliases>
<!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
<package name="com.it.mybatis.pojo" />
</typeAliases>
<mappers>
<package name="com.it.mybatis.mapper"/>
</mappers>
</configuration>
- 配置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>
- 书写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
- 添加日志文件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开发
-
创建pojo类
-
创建dao接口
-
创建dao实现类
- 要继承SqlSessionDaoSupport
4.4. 使用Mapper代理形式开发dao
- 创建xxxMapper.java接口
- 主要定义方法,方法名和xxxMapper.xml的id相同
- 配置xxxMpper.xml
- 这里面配置的是数据库的书写,返回类型,传参类型,方法对应id。和前面讲的一样。
- 配置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>
- 测试代码
- 运行前初始化,获取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. 动态代理加强
-
加强的目的
- 如果不加强的话,每xxxMapper.java一个接口都要在applicationContext.xml中将其配置到MapperFactoryBean中,因为测试代码要通过id值获取这个Bean。
- 那么你写一个接口就要重新配置一个Bean,如果接口有几百个就要写几百个Bean。这样肯定是不行的。
-
配置MapperScannerConfigurer 容器,
- 这个容器可以自动扫描,我们只需要指定该接口在哪个包下面就行了。它会自动扫描。
- 该容器还会扫描sqlSessionFactory,也就是受在applicationContext中这个也不用配了。
- 主要要指定name为基础包basePackage
<!-- Mapper代理的方式开发方式二,扫描包方式配置代理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置Mapper接口 -->
<property name="basePackage" value="cn.it.mybatis.mapper" />
</bean>
- 测试类
- 先获取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);
}