一句话介绍Mybatis:
Mybatis是一个优秀的持久层框架,它对jdbc操作数据库的过程进行封装,使开发不需要花费精力去处理使用原生JDBC时要进行的操作(注册驱动、创建connection、statement等)。
一句话概括Mybatis工作机制:
Mybatis通过xml或注解配置statement(拼装sql),并通过与java对象进行映射生成最终执行的sql语句,交由框架执行后结果映射成java对象并返回。
※一个入门的Mybatis增删改查案例:
数据库表结构:
测试数据:
创建的工程结构如下(eclipse):
lib中需要导入Mybatis所需要的依赖包,并build path,而config目录下放的是相关的配置文件:
1.JDBC的配置文件——jdbc.properties:
2.框架的配置文件——mybatis.xml
框架的配置文件名称可以随便,在xml文件头只要引入mybatis框架的约束文件路径即可:
PS: 上面的mybatis-3-config.dtd即为xml格式文件的约束文件,引入它就规定了xml文件所使用的标签以及标签之间的关系。
下面是mybatis框架配置所要用到的基本配置标签:
1.configuration——根标签,即所有其他的配置标签都在包含在configuration中
2.properties——规定了所要加载读取的指定路径的properties文件,比如:
即代表框架需要从jdbc.propterties中读取到数据库连接信息
3.environments——环境标签,可以看出这里的environments是复数的,即mybatis中可以配置多个环境标签,不同环境标签使用id属性区别。之所以支持配置多个环境标签,是因为有助于将SQL映射成应用于多种数据库中,enviroments标签下,需要配置的子标签如下:
1.environment标签——配置单个环境,不同的环境以id区别
2.transactionManager——事务管理器标签,type属性可以选择两种类型的事务管理器:JDBC/MANAGED
JDBC事务管理机制:直接使用java.sql.Connection对象完成对事务的提交、回滚等操作
MANAGED事务管理机制:使用WEB容器来管理事务
3.dataSource——数据源,type属性中可以选择3种数据源类型:UNPOOLED/POOLED/JNDI
UNPOOLED:表示Mybatis会为每一次的数据操作都去创建一个新的连接,并关闭它,适用于小规模简单程序上。
POOLED:Mybatis会去创建一个数据库连接池,池中的每个连接会被用于数据库操作,且操作完成时该连接会被返还给连接池。适用于测试开发环境。
JNDI:Mybatis从应用服务器向配置好的数据源获取数据库连接。适用于生产环境。
在dataSource标签中,通过property子标签来配置数据库的连接信息(name属性需要与jdbc.properties中所配置的属性对应,通过EL表达式取值)
此处我们的框架的配置信息就写好了,完整配置如下:
PS:除此之外,最后我们还要有一个mapper标签,在里面引入对象和数据库表的映射文件,由于映射文件还没写,这在后面再说,先来配置该映射文件。
在配置映射文件之前,由于mybatis是将数据库字段映射成对象的框架,所以我们还需要有对象实体:
该实体类包含的属性(一对set/get)名要和数据库表中的字段名一一对应,但数据库表中有的字段,实体类中不一定要有,看实际需求,这里就取3个字段(id,uname,age),而数据库表中有4个字段(id,uname,area,age):
此处省略set/get方法
接着再开始配置映射文件:
1.在config目录下创建user.xml(这里先暂时在config目录下创建,在后面会介绍另一种便于批量引入的映射文件的创建方式),引入mybatis映射文件的约束头:
接着是mybatis框架mapper文件配置所要用到的基本配置标签:
1.mapper标签——其属性namespace,用于对sql进行分类化管理:
PS:若使用Dao开发方式,映射文件的namespace任意命名;若使用Mapper接口代理的方式开发,namespace必须为接口的全名(一般规范为XxxDao.xml,然后namespace命名为包路径.XxxDao)。这两种开发方式在后面会介绍到,这里暂时任意填写。
然后我们在mapper标签中拼装要用到的sql语句:
1.通过id来进行查询:
解释:
首先是select——表示这是一个映射查询语句,其中有几个属性:
1.id——作为标识符,可以被用来引用这条语句,是唯一的。
2.parameterType——表明传入这条语句的参数类的完全限定名或别名,比如根据id查询,需要传入的id值是一个int类型,那么其包装类的全限定名即为java.lang.Integer。还有需要写别名的情况在后面会介绍到。
3.resultType——表示这条语句期望返回的类型,比如我通过id查询,返回的结果需要用一个User实体是接收它,那么期望类型就是model.User(全限定名称)。
除了select外,其余的元素为:
insert-映射插入语句
update-映射更新语句
delete-映射删除语句
select-映射查询语句
而对于#{}符号,其实是一个占位符,表示mybatis框架会接收输入的参数并赋值到sql语句当中。
这样,一个完整的映射就完成了,将mapper文件引入到上面配置文件的mapper中去:
进行测试,步骤如下:
@Test
public void testGetFromId() throws Exception {
//1.获取核心配置文件的数据
InputStream in = Resources.getResourceAsStream("mybatis.xml");
//2.获取会话工厂(会话就是连接)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取会话对象(相当于jdbc当中的connection)
SqlSession session = factory.openSession();
//4.操作数据库
int id = 2;
User user = (User)session.selectOne("model.User.findUserById",id);
System.out.println(user );
session.close();
}
上述的测试代码中,Resources是由Mybatis提供的专门用于读取配置文件(即mybatis.xml),并返回一个输入流对象,由于配置文件在源目录下,所以参数直接写mybatis.xml,不能写成config/mybatis.xml.因为config本身也是一个源目录,所有源目录中的文件进行编译后时放在同一个的目录中的。整个连接的过程其实和JDBC的思路是一样的。
将会话对象的获取过程封装成一个MybatisUtil:
public class MybatisUtil {
private static SqlSessionFactory factory;
static{
//1.获取核心配置文件的数据
InputStream in;
try{
in = Resources.getResourceAsStream("mybatis.xml");
//2.创建会话工厂
factory = new SqlSessionFactoryBuilder().build(in);
}catch(IOException e){
e.printStackTrace();
}
}
public static SqlSessionFactory getFactory(){
return factory;
}
}
测试代码:
@Test
public void testGetFromId() throws Exception {
SqlSession session = MybatisUtil.getFactory().openSession();
int id = 2;
User user = (User)session.selectOne("model.User.findUserById",id);
System.out.println(user );
session.close();
}
selectOne方法:查询一条记录,不能用于查询多条记录,否则报错。其中第一个参数由namespace和对应的id属性唯一决定,第二个参数表示要传入到sql语句中的参数。
该方法的返回值是一个SqlSession,需要把它强转成User类型
测试结果:
一个简单的查询案例完成。
需求2:根据用户名查询用户信息(包含模糊查询like):
注意:这里使用的不是#{}而是${},${value}表示使用参数将该值替换,用于字符串拼接,比如传进来的参数为test,那么此处拼接结果为%test%
测试:
@Test
public void testGetFromName() throws Exception {
SqlSession session = MybatisUtil.getFactory().openSession();
String name = "test";
List list = session.selectList("model.User.findUserByName",name);
for (User user : list) {
System.out.println(user);
}
session.close();
}
由于查询结果可能有多条语句,即可能有多个对象,所以要用一个List
selectList可以查询一条或多条语句
测试结果:
需求3:添加用户
insert into user values(null,#{uname},null,#{age});
插入不需要期望返回结果,插入的类型为User,对于不需要插入的值(比如主键策略为自增,就不需要插入主键)设置为null
测试:
SqlSession session = MybatisUtil.getFactory().openSession();
//mybatis框架默认非自动提交事务,如果进行增删改操作时,需要手动提交事务
User user = new User();
user.setUname("test05");
user.setAge(22);
session.insert("model.User.addUser",user);
//提交事务
session.commit();
session.close();
注意:mybatis框架默认为非自动提交事务,所以进行增删改操作时,需要手动提交事务
测试结果:
而在实际需求中,由于主键策略为自动递增,我们有时要拿到插入对象形成记录的主键,这是可以用到主键回填标签:
select LAST_INSERT_ID()
insert into user values(null,#{uname},null,#{age});
添加selectKey将主键返回,其中:
order:selectKey的执行顺序,是相对于insert语句来说的,由于mysql的自增原理,执行完insert语句之后才将主键生成,所以顺序为after
keyProperty:指定返回主键存储在pojo中的哪个属性
resultType:返回的主键类型
LAST_INSERT_ID():这是一个mysql函数,返回auto_increment自增列记录的id值
需求4:更新用户
update user set uname=#{uname},age=#{age} where id=#{id}
测试:
SqlSession session = MybatisUtil.getFactory().openSession();
User user = new User();
user.setId(2);
user.setUname("test02");
user.setAge(18);
session.update("model.User.updateUser", user);
session.commit();
session.close();
测试结果:
需求5:根据id删除用户
delete from user where id=#{id}
测试:
SqlSession session = MybatisUtil.getFactory().openSession();
int id = 5;
session.update("model.User.deleteUser", id);
session.commit();
session.close();