mybatis本是apache的一个开源项目iBatis,2010年这个项目由apache software迁移到了google code,并且改名为mybatis,实质上对ibatis进行一些改进。目前mybatis在github上托管。
mybatis是持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,不需要花费精力去处理其他东西。
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
SqlMapConfig.xml中配置的内容顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
mybatis的全局配置文件,名称不固定,主要配置的是运行环境(数据源、事物),设置全局参数,设置别名
引入数据源外部文件:
设置全局参数:
设置别名:
配置数据库:(和spring整合后不需要了)
引入mapper.xml文件(该文件主要配置的是sql语句):
mybatis有两种开发dao的方法:(1)原始开发dao方式(dao接口和dao实现都需要编写)(2)mapper代理方式(只需要写dao接口)
mapper.xml文件编写:
需要编写dao接口和dao的实现类
dao接口:
public interface UserDao {
public User findUserById(int id) throws Exception;
}
dao实现类:
public class UserDaoImpl implements UserDao{
//工厂类
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
super();
this.sqlSessionFactory = sqlSessionFactory;
}
public User findUserById(int id) throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
//根据id查询信息
// 如果查询结果返回的是多条记录,使用selectOne();
User user = sqlSession.selectOne("test.findUserById", 1);
sqlSession.close();
return user;
}
}
原始开发dao配置文件创建SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException{
// 全局配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
原始开发dao获取测试
@Test
public void testFindUserById(){
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserById(1);
System.out.println(user);
}
程序员只需要写dao接口,到接口实现对象由mybatis自动生成代理对象。
原始开发dao方式存在问题:
1、dao的实现类中 存在重复代码,整个mybatis操作的过程代码模块重复(先创建sqlSession、调用sqlsession的方法、关闭sqlsession)
2、dao的实现类中存在硬编码,调用sqlsession方法时将statement的id硬编码。
mapper.xml文件和mapper.java文件(每个方法输入参数只能是一个,想输入多个通过扩展pojo)编写:
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中方法返回值类型一致
public interface UserMapper {
public User findUserById(int id) throws Exception;
5、mapper接口中返回单个对象和集合对象
不管查询记录是单条还是多条,在statement中resultType定义一致,都是单条记录映射的pojo类型。
mapper接口方法返回值,如果返回的单个对象,返回值类型是pojo类型,生成的代理对象内部通过selectOne获取记录,如果返回值类型是集合对象,生成的代理对象内部通过selectList获取记录
public interface UserMapper {
// 调用selectOne获取记录
public User findUserById(int id) throws Exception;
// 调用selectList获取记录
public List findUserByName(String username) throws Exception;
6、返回值问题
调用statement,返回多条记录,mapper.java中方法的返回类型不能是pojo类型,因为是pojo类型,底层会调用selectOne函数查询结果,会报错!!
7、输入参数问题(解决Mapper.java中方法只能输入一个参数的问题)
可以通过扩展pojo(自己定义pojo包装类型,将要传入的参数传入进去。)
public List findUserList(UserQuery userQuery) throws Exception;
public class UserQueryVo {
private User user;
private UserCustom userCustom;
private List ids;
public class UserCustom extends User {
}
//测试findUserList
@Test
public void testFindUserList() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserQueryVo userQueryVo = new UserQueryVo();
UserCustom userCustom = new UserCustom();
userCustom.setUsername("小明");
userQuery.setUserCustom(userCustom);
List list = userMapper.findUserList(userQueryVo);
sqlSession.close();
System.out.println(list);
}
mybatis开发小结:
1、编写SqlMapConfig.xml :引入数据库文件,引入mapper.xml文件
2、编写mapper.xml文件:定义statement,封装成MappedStatement对象返回。
3、编程通过配置文件创建SqlSessionFactory
4、通过sqlSessionFactory获取SqlSession
5、通过SqlSession操作数据库
6、关闭SqlSession!!
因为插入记录需要一个主键返回:有两种方式
mapper.xml文件中
通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select LAST_INSERT_ID()就可以获取自增主键
通过使用mysql的uuid机制生成主键:使用uuid生成主键的好处是不考虑数据库移植后主键冲突问题。
select uuid();
Insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
作用:创建SqlSessionFactory
作用:操作数据库
executor是一个接口,有两个实现(默认执行器和缓存执行器)
作用:封装sql语句
在mapper.xml中封装的sql语句返回的就是mappedStatement对象
每一个select就是mappedStatement对象
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定义输出结果的类型。
resultType:指定输出结果的类型,将sql查询结果映射为java对象。
注意:使用resultType时,sql查询的列名要和resultType指定的pojo的属性名相同,指定相同的属性才可以映射成功,如果sql查询的列名要和resultType指定的pojo的属性名全部不相同,list中无法创建pojo对象。
resultMap:将查询结果映射为java对象
如果查询列名和最终要映射的pojo的属性名不一致,使用resultMap将列名和pojo的属性名做一个对应关系(列名和属性名映射设置)
resultMap
使用resultMap
#{}和${}完成输入参数的属性值获取,通过OGNL获取parameterType指定pojo的属性名。
#{}:占位符号,好处防止sql注入
${}:sql拼接符号
if和where
通过sql片段可以将sql语句抽取出来,单独定义,在其他statement中可以引用sql片段。
and username like '%${userCustom.username}%'
and sex = #{userCustom.sex}
// 这个查询相当于
select * from user where username like '%${userCustom.username}%' and sex = #{userCustom.sex};
在statement通过foreach遍历parameterType中的集合类型
//相当于
select id,username,birthday from user where username like '%小明%' and id in(id1,id2,id3);
//相当于
select id,username,birthday from user where username like '%小明%' and (id = 16 or id = 24 or id = 36);
当关联多个表进行查询的时候,一般resultType无法完成,多使用resultMap完成
// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;
根据查询语句的返回类型确定扩展的pojo类型
public class OrderCustom extends Orders{
//补充用户信息
private String username;
private String sex;
}
mapper.xml中编写:
mapper.java
public interface OrdersMapperCustom{
public List findOrderUserList() throws Exception;
}
resultMap提供一对一关联查询的映射和一对多关联查询映射,一对一映射思路:将关联查询的信息映射到pojo中。
下列查询语句就是讲关联信息的pojo映射到主查询信息pojo中
// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;
public class Orders{
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//关联用户信息
private User user;
}
mapper.xml
resultMap定义:
//SqlMapConfig.xml中定义别名 没有定义别名记得写全限定名
resultType:要自定义pojo保证sql查询列和pojo的属性对应,这种方法简单
resultMap:使用association完成一对一映射需要设置一个resultMap,如果要实现延迟加载就只能用resultMap实现。
// 查询语句:select orders.*,user.username,user.sex from orders,user where orders.user_id = user.id;
//在这个语句的基础上加一个表关联
select orders.*,user.username,user.sex,orderdetail.id ordertail_id,orderdetail.items_num,orderdetail.items_id from orders,user,orderdetail where orders.user_id = user.id and orders_id = orderdetail.orders_id;
public class Orders{
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//关联用户信息
private User user;
private List orderdetails;
}
resultMap定义:
//注意这里用的是collection !! 不是association