**
mybatis第一天:基础知识
**
mybatis是一个java持久层框架,java中操作关系型 数据库用的是jdbc,mybatis是对jdbc的一个封装。
1、从一个jdbc程序开始,通过jdbc 程序找到使用原生态的jdbc开发程序,存在哪些问题??通过学习mybatis,mybatis是如何解决这些问题。
2、mybatis的架构(重点)
3、mybatis的入门程序(重点)
实现 用户的查询、添加、修改、删除。
4、mybatis开发dao的两种方法。(重点)
原始的dao开发方式(dao接口和dao实现都需要编写)
mapper代理方式(只需要写dao接口)
5、输入映射类型和输出映射类型。
6、动态sql
第二天:高级知识
高级映射查询(一对一、一对多、多对多)(重点)
查询缓存
延迟加载
mybatis和spring整合(重点)
mybatis逆向工程 。
1 jdbc编程中问题
企业开发中,根据项目大小、特点进行技术选型 ,jdbc操作数据库时效率是很高的,jdbc也是技术选型的参考。
1.1 jdbc问题总结
1、数据库连接频繁的创建和关闭,缺点浪费数据库的资源,影响操作效率
设想:使用数据库连接池
2、sql语句是硬编码,如果需求变更需要修改sql,就需要修改java代码,需要重新编译,系统不易维护。
设想:将sql语句 统一配置在文件中,修改sql不需要修改java代码。
3、通过preparedStatement向占位符设置参数,存在硬编码( 参数位置,参数)问题。系统不易维护。
设想:将sql中的占位符及对应的参数类型配置在配置文件中,能够自动输入映射。
4、遍历查询结果集存在硬编码(列名)。
设想:自动进行sql查询结果向java对象的映射(输出映射)。
2 Mybatis架构(重点)
2.1 mybatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。 目前mybatis在github上托管。git(分布式版本控制,当前比较流行)
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
2.2 mybatis架构
3 mybatis入门程序
3.1 需求
实现用户查询:
根据用户id(主键)查询用户信息(单条记录)
根据用户名称模糊查询用户信息(多条记录)
用户添加
用户删除
用户修改
3.2 导入jar包
从mybatis管网下载(地址:https://github.com/mybatis/mybatis-3/releases)
mybatis-3.2.7.pdf—操作手册
mybatis-3.2.7.jar–核心 jar包
依赖的jar包
别忘记了jdbc的jar
3.3 工程结构
3.4 log4j.properties(公用文件)
#Global logging configuration,建议开发环境中要用debug
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.5 SqlMapConfig.xml(公用文件)
通过SqlMapConfig.xml加载mybatis运行环境。
<?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>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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="sqlMap/UserMapper.xml"/>-->
<mapper resource="com/baidu/dao/UserMapper.xml"/>
</mappers>
</configuration>
3.6 根据id查询用户
3.6.1 pojo(User.java)
3.6.2 User.xml(重点)
建议命名规则:表名+mapper.xml 早期ibatis命名规则:表名.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的 但是后期会有其他作用-->
<mapper namespace="test">
<select id="selectUser" resultType="com.baidu.pojo.User" >
select * from t_user
</select>
<select id="findByUserById" parameterType="int" resultType="com.baidu.pojo.User" >
select * from t_user where id=${value}
</select>
<insert id="insertUser" parameterType="string">
insert into t_user set name='${value}'
</insert>
<!--如果传递的是字面值常量 那么#{名称可以随意}-->
<insert id="insertUser2" parameterType="string">
insert into t_user set name=#{aaa}
</insert>
<!--如果传递的是自定义对象 那么#{名称必须是对象中的getXXX}-->
<insert id="insertUser3" parameterType="com.baidu.pojo.User">
insert into t_user set name=#{name}
</insert>
<insert id="insertUser4" parameterType="com.baidu.pojo.User" useGeneratedKeys="true" keyProperty="id" >
<!-- <selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>-->
insert into t_user set name=#{name}
</insert>
<update id="updateUser" parameterType="com.baidu.pojo.User">
update t_user set name=#{name} where id=#{id}
</update>
<!--select * from t_user where name like '%${value}%'-->
<select id="findByNameLike" parameterType="string" resultType="com.baidu.pojo.User">
select * from t_user where name like #{name}
</select>
</mapper>
3.6.3 编码
创建SqlSessionFactory:
SqlSessionFactory sqlSessionFactory;
@Before
public void befroe() throws IOException {
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
this.sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
}
创建测试类:
@Test
public void testFindById(){
SqlSession session = sqlSessionFactory.openSession();
User o = (User)session.selectOne("test.findByUserById", 1);
System.out.println(o);
session.close();
}
3.7 根据用户名称模糊查询用户信息
根据用户名称模糊查询用户信息可能返回多条记录。
3.7.1 User.xml
<select id="findByNameLike" parameterType="string" resultType="com.baidu.pojo.User">
select * from t_user where name like #{name}
</select>
3.7.2 编码
@Test
public void testfindByNameLike(){
SqlSession session = sqlSessionFactory.openSession();
List<Object> list = session.selectList("test.findByNameLike", "%aa%");
System.out.println(list.size());
session.close();
}
3.7.3 使用${}接收参数
3.8 mybatis开发过程小结
1、编写SqlMapConfig.xml 2、编写mapper.xml 定义了statement
3、编程通过配置文件创建SqlSessionFactory 4、通过SqlSessionFactory获取SqlSession
5、通过SqlSession操作数据库 如果执行添加、更新、删除需要调用SqlSession.commit()
6、SqlSesion使用完成要关闭
3.9 用户添加
向用户表插入一条记录。
3.9.1 User.xml
<insert id="insertUser4" parameterType="com.baidu.pojo.User" useGeneratedKeys="true" keyProperty="id" >
<!-- <selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>-->
insert into t_user set name=#{name}
</insert>
3.9.2 编码
@Test
public void testInsertUser(){
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setName("lisisisisi");
session.insert("test.insertUser4", user);
System.out.println(user.getId());
session.commit();
session.close();
}
3.9.3 主键返回
需求:user对象插入到数据库后,新记录的主键要通过user对象返回,通过user获取主键值。
解决思路: 通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select
LAST_INSERT_ID()就可以获取自增主键。
- User.xml修改: 方式一(针对不同数据都可以):
- 方式二(常用):
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
主键必须是自动生成的,是int,long 使用mysql的uuid机制生成主键:
使用uuid生成主键的好处是不考虑数据库移植后主键冲突问题。
实现思路: 先查询uuid得到主键,将主键设置到user对象中,将user对象插入数据库。
实现 oracle数据库主键返回,如何做??oracle没有自增主键机制,使用序列完成主键生成。
实现思路: 先查询序列得到主键,将主键设置到user对象中,将user对象插入数据库。
3.10 用户删除和更新
省略…
3.10.1 编码
省略…
3.11 Mybatis解决jdbc编程的问题
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
3.12 mybatis与hibernate重要区别
企业开发进行技术选型 ,考虑mybatis与hibernate适用场景。
mybatis:入门简单,程序容易上手开发,节省开发成本 。mybatis需要程序员自己编写sql语句,是一个不完全的ORM框架,对sql修改和优化非常容易实现 。
mybatis适合开发需求变更频繁的系统,比如:互联网项目。
hibernate:入门门槛高,如果用hibernate写出高性能的程序不容易实现。hibernate不用写sql语句,是一个 ORM框架。
hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。
总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统 可维护性为出发点进行技术选型。
3.13 总结
3.13.1 SqlMapConfig.xml
是mybatis全局配置文件,只有一个,名称不固定的,主要全局配置,加载mapper.xml中配置 sql语句
3.13.2 mapper.xml
mapper.xml是以statement为单位进行配置。(把一个sql称为一个statement),satatement中配置 sql语句、parameterType输入参数类型(完成输入映射)、resultType输出结果类型(完成输出映射)。
还提供了parameterMap配置输入参数类型(过期了,不推荐使用了)
还提供resultMap配置输出结果类型(完成输出映射),明天重点讲通过resultMap完成复杂数据类型的映射(一对多,多对多映射)
3.13.3 #{}
表示一个占位符,向占位符输入参数,mybatis自动进行java类型和jdbc类型的转换。
程序员不需要考虑参数的类型,比如:传入字符串,mybatis最终拼接好的sql就是参数两边加单引号。
#{}接收pojo数据,可以使用OGNL解析出pojo的属性值
3.13.4 ${}
表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。
${}也可以接收pojo数据,可以使用OGNL解析出pojo的属性值
缺点:不能防止sql注入。
3.13.5 selectOne
用于查询单条记录,不能用于查询多条记录,否则异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
3.14 selectList
用于查询多条记录,可以用于查询单条记录的。
4 mybatis开发dao的方法
4.1 SqlSession作用范围
是使用局部变量、成员变量。。。。???
4.1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder是以工具类方式来使用,需要创建sqlSessionFactory就new一个SqlSessionFactoryBuilder。
4.1.2 sqlSessionFactory
正常开发时,以单例方式管理sqlSessionFactory,整个系统运行过程中sqlSessionFactory只有一个实例,将来和spring整合后由spring以单例方式管理sqlSessionFactory。
4.1.3 SqlSession
sqlSession是一个面向用户(程序员)的接口,程序员调用sqlSession的接口方法进行操作数据库。
sqlSession能否以单例方式使用??
由于sqlSession是线程不安全,所以sqlSession最佳应用范围在方法体内,在方法体内定义局部变量使用sqlSession。
4.2 原始dao开发方式
程序员需要写dao接口和dao 的实现 类
4.2.1 dao接口
略
4.2.2 dao接口实现
public class UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDao(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory=sqlSessionFactory;
}
public void add(){
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setName("lisisisisi");
session.insert("test.insertUser4", user);
System.out.println(user.getId());
session.commit();
session.close();
}
}
4.2.3 测试代码
@Test
public void test(){
UserDao userDao = new UserDao(sqlSessionFactory);
userDao.add();
}
4.3 mapper代理的方式(推荐)
程序员只需要写dao接口,dao接口实现对象由mybatis自动生成代理对象。
本身dao在三层架构中就是一个通用的接口。
4.3.1 上边原始dao开发方式的问题
1 dao的实现类中存在重复代码,整个mybatis操作的过程代码模板重复(先创建sqlsession、调用sqlsession的方法、关闭sqlsession)
2、dao的实现 类中存在硬编码,调用sqlsession方法时将statement的id硬编码。
4.3.2 mapper开发规范
要想让mybatis自动创建dao接口实现类的代理对象,必须遵循一些规则:
1、mapper.xml中namespace指定为mapper接口的全限定名
此步骤目的:通过mapper.xml和mapper.java进行关联。
2、mapper.xml中statement的id就是mapper.java中方法名
3、mapper.xml中statement的parameterType和mapper.java中方法输入参数类型一致
4、mapper.xml中statement的resultType和mapper.java中方法返回值类型一致
4.3.3 mapper.xml(映射文件)
mapper映射文件的命名方式建议:表名Mapper.xml
namespace指定为mapper接口的全限定名
<select id="findByName" parameterType="com.baidu.pojo.User" resultType="com.baidu.pojo.User">
select * from t_user where name like #{name}
</select>
4.3.4 mapper接口
mybatis提出了mapper接口,相当 于dao 接口。
mapper接口的命名方式建议:表名Mapper
public interface UserMapper {
List<User> findByName(User user);
}
4.3.5 将mapper.xml在SqlMapConfig.xml中加载
<mappers>
<!--<mapper resource="sqlMap/UserMapper.xml"/>-->
<mapper resource="com/baidu/dao/UserMapper.xml"/>
</mappers>
4.3.6 mapper接口返回单个对象和集合对象
不管查询记录是单条还是多条,在 statement中resultType定义一致,都是单条记录映射的pojo类型。
mapper接口方法返回值,如果是返回的单个对象,返回值类型是pojo类型,生成的代理对象内部通过selectOne获取记录,如果返回值类型是集合对象,生成的代理对象内部通过selectList获取记录。
4.3.7 问题
4.3.7.1 返回值的问题
如果方法调用的statement,返回是多条记录,而mapper.java方法的返回值为pojo,此时代理对象通过selectOne调用,由于返回多条记录,所以报错:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
4.3.7.2 输入参数的问题
使用mapper代理的方式开发,mapper接口方法输入 参数只有一个,可扩展性是否很差??
可扩展性没有问题,因为dao层就是通用的,可以通过扩展pojo(定义pojo包装类型)将不同的参数(可以是pojo也可以简单类型)传入进去。
5 sqlMapConfig.xml
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)---》mybaties有一个分页插件
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
5.1 properties属性定义
可以把一些通用的属性值配置在属性文件中,加载到mybatis运行环境内。
比如:创建db.properties配置数据库连接参数。
注意: MyBatis 将按照下面的顺序来加载属性:
在 properties 元素体内定义的属性首先被读取。
然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
最后读取parameterType传递的属性,它会覆盖已读取的同名属性。
建议使用properties,不要在properties中定义属性,只引用定义的properties文件中属性,并且properties文件中定义的key要有一些特殊的规则。
5.2 settings全局参数配置
mybatis运行时可以调整一些全局参数(相当于软件的运行参数),参考:mybatis-settings.xlsx
根据使用需求进行参数配置。
注意:小心配置,配置参数会影响mybatis的执行。
ibatis的全局配置参数中包括很多的性能参数(最大线程数,最大待时间。。。),通过调整这些性能参数使ibatis达到高性能的运行,mybatis没有这些性能参数,由mybatis自动调节。
5.3 typeAliases(常用)
可以将parameterType、resultType中指定的类型通过别名引用。
5.3.1 mybaits提供了很多别名
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
5.3.2 自定义别名
<!--类型转换器-->
<typeAliases>
<!--单独制定别名-->
<!-- <typeAlias type="com.baidu.pojo.User" alias="uuu"></typeAlias>-->
<package name="com.baidu.pojo"></package>
<!--首字母大写或者小写均可-->
</typeAliases>
5.3.3 使用别名
在parameterType、resultType中使用别名:
5.3.4 typeHandlers
类型处理器将java类型和jdbc类型进行映射。
mybatis默认提供很多类型处理器,一般情况下够用了。
5.3.5 mappers
6 输入和输出映射
通过parameterType完成输入映射,通过resultType和resultMap完成输出映射。
6.1 parameterType传递pojo包装对象
可以定义pojo包装类型扩展mapper接口输入参数的内容。
需求:
自定义查询条件查询用户信息,需要向statement输入查询条件,查询条件可以有user信息、商品信息。。。。
6.1.1 包装类型
6.1.2 mapper.xml
6.1.3 mapper.java
6.1.4 测试
6.1.5 异常
如果parameterType中指定属性错误,异常,找不到getter方法:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userCusto' in 'class com.baidu.mybatis.po.UserQueryVo'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userCusto' in 'class com.baidu.mybatis.po.UserQueryVo'
注意:如果将来和spring整合后,不是通过调用getter方法来获取属性值,通过反射强读取pojo的属性值。
6.2 resultType
指定输出结果的类型(pojo、简单类型、hashmap…),将sql查询结果映射为java对象 。
6.2.1 返回简单类型
mapper.xml
mapper.java
注意: 如果查询记录结果集为一条记录且一列再使用返回简单类型。
6.3 resultMap(入门)
resultType :指定输出结果的类型(pojo、简单类型、hashmap…),将sql查询结果映射为java对象 。
使用resultType注意:sql查询的列名要和resultType指定pojo的属性名相同,指定相同属性方可映射成功,如果sql查询的列名要和resultType指定pojo的属性名全部不相同,list中无法创建pojo对象的。
resultMap:将sql查询结果映射为java对象。
如果sql查询列名和最终要映射的pojo的属性名不一致,使用resultMap将列名和pojo的属性名做一个对应关系 (列名和属性名映射配置)
6.3.1 resultMap配置
6.3.2 使用resultMap
6.3.3 mapper.java
7 动态sql(重点)
mybatis重点是对sql的灵活解析和处理。
7.1 需求
将自定义查询条件查询用户列表和查询用户列表总记录数改为动态sql
7.2 if和where
<!--where标签相当于where关键字 可以自动去除第一个and-->
<select id="findList" parameterType="user" resultType="user">
select * from t_user
<where>
<if test="null != name">
and name=#{name}
</if>
<if test="null != id">
and id=#{id}
</if>
//还可以写更多的条件
</where>
</select>
7.3 sql片段
通过sql片段可以将通用的sql语句抽取出来,单独定义,在其它的statement中可以引用sql片段。
通用的sql语句,常用:where条件、查询列
7.3.1 sql片段的定义
<sql id="queryUserWhere">
<if test="null != name">
and name=#{name}
</if>
<if test="null != id">
and id=#{id}
</if>
</sql>
7.3.2 引用sql片段
<!--where标签相当于where关键字 可以自动去除第一个and-->
<select id="findList" parameterType="user" resultType="user">
select * from t_user
<where>
<include refid="queryUserWhere"></include>
</where>
</select>
7.4 foreach
在statement通过foreach遍历parameterType中的集合类型。
需求:
根据多个用户id查询用户信息。
7.4.1 在user中定义list ids
在user中定义list ids存储多个id
7.4.2 修改where语句
使用foreach遍历list:
方式1:
方式2:
7.4.3 测试代码