在我们的日常开发中MyBatis是使用的比较多的orm框架之一,以前本患者仅仅停留在会用的层面上,但是对于一些原理性的东西基本都是知其然不知其所以然。所以最近这段时间打算和大家一起讨论一下MyBatis的一些核心原理。
首先今天先和大家分享一下本人使用MyBatis框架的两种方法,一种是基于注解的方式,另外一种是编写xml映射文件,首次我们来到官网:https://mybatis.org/mybatis-3/ 进入之后是如下图所示的界面:
从上图中可以看到MyBatis的基本介绍,但是最重要的一点是下方的五星红旗,激动吧,MyBatis官方网站上支持中文版本的,因此我们点击五星红旗,切换到中文版本,如下图所示:
我们首先点击上图中的项目的GIT代码库 进入到MyBatis 的下载页面,如下图所示:
首先我们点击下载最新的版本,进入到如下图所示的界面:
点击下方的zip包的超链接,下载MyBatis框架的包, 其中mybatis-3.5.3这个是可运行的文件,下面的两个是源码文件包。下载完成之后我们解压mybatis-3.5.3这个包,解压后的目录如下所示:
其中我们pdf就是MyBatis的使用手册,mybatis-3.5.3.jar就是我们需要导入到程序中的jar包,lib目录下存放的是我们可能要用到的一些jar包,并不是Mybatis依赖的jar包.
好了,关于mybatis 的下载就先到这里了,我们先来写一个入门级的demo。
首先打开我们的开发工具,创建一个新的Java工程,并创建相应的包结构。如下图所示(开发工具暂时用的sts):
其中我们在lib目录下导入数据库驱动包和Mybatis包,同时我们还需要一个日志的工具,这里我们使用log4g,日志包可以在Mybatis框架的lib目录中找到。conf目录是存放配置文件的目录。创建好了之后我们在pojo中新建一个实体类,Fruit,该类中易功有四个属性,分别是id,name,area,color,同时我们为这四个属性创建set/get方法,以及toString方法。具体代码如下图所示:
private Integer id;
private String fruitName;
private String area;
private String fruitColor;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getFruitColor() {
return fruitColor;
}
public void setFruitColor(String fruitColor) {
this.fruitColor = fruitColor;
}
@Override
public String toString() {
return "Fruit [id=" + id + ", fruitName=" + fruitName + ", area=" + area + ", fruitColor=" + fruitColor + "]";
}
接着我们需要准备数据了,打开mysql ,新建一个数据库mybatis,然后再新建一张fruit表,表结构如下所示:
好了,数据库环境准备完成之后我们就回到开发工具中,准备我们的第一个demo了。
emmmm.........有点不知道怎么下手,还是先打开我们下载的zip包中的pdf文档吧,我们打开文档之后选择 Getting Started
然后可以看到如下内容 :
这里说的意思就是从xml来构建SqlSessionFactory这个对象,并且给了一段示例的代码 ,这段代码的意思就是获取SqlSessionFactory这个对象,首先我们把这段代码复制到我们的工程中,我们知道这段代码肯定是会产生一个SqlSessionFactory对象的,因此我们新建一个util包,在该包中新建一个工具类,将上述示例代码封装成一个方法,代码如下:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
}
可以看到,上述代码实在加载一个xml文件,而SqlSessionFactory对象是根据这个配置文件来创建 的,所以我们也需要一个mybatis-config.xml的配置文件,好了,回到官方文档上,我们可以看到这样一句话:
我们可以看到 上面说从 XML 文件中构建 SqlSessionFactory 的实例非常简单,我们可以用任意的InputStream实例来加载该文件 同时可以用字符串形式的xml或者在文件系统中使用任意的 URL 形式指定路径的文本文件都可以。但是他们建议我们在类路径下创建该文件,大概的意思看懂了就可以,这里就不逐字逐句的给大家翻译了 (英语能力实在有限,详细的中文意思推荐去官网上看)。 所以我们同从官方的建议,在类路径conf下创建一个mybatis-config.xml文件,这里解释一下conf默认和类路径一致。
那么创建好了之后我们需要在该配置文件中写一些什么东西呢? 好了 ,我们回到文档上,会发现文档上提供了一个样例。如下图所示:
我们将上述的示例复制到我们创建的xml中去。如下图所示:
按照我们以往的开发经验来猜测,dataSource标签中的内容应该是和数据源相关的,因此这里我们将自己数据库的信息写上去即可。那么还有一个mapper标签的中的内容应该写什么呢? 我们看到官方给出的也是一个xml文件,好了,我们继续看文档,发现文档中还有一种方式构建SqlSessionFactory,不使用xml配置文件:
这里先放一放,稍后再给大家演示这种方式。我么继续往下看文档,发现后面是告诉我们怎么从SqlSessionFactory中获取 SqlSession对象,同样的给我们提供了一段简单的代码示例,如下所示:
我们继续将上述示例代码拷贝到我们的工具类中,封装成一个方法,如下图所示:
到这里我们应该有一种直觉,Blog肯定是一个pojo对象,理论上应该对应一张数据表,好了,我们继续往下看文档,发现下面还有一段示例代码,如下图所示:
可以看到官方更推荐下面这种方式的用法,使用一个接口来完成查询的操作,该接口定义了查询语句的参数和查询的结果类型。好了,我们继续将上述代码拷贝到工具类中单独封装成方法,稍后我们逐一来演示这两种方式。接着我们尝试将Blog修改成本次我们自己写的实体类Fruit,修改完成之后我们的工具类CommomUtils完整的代码如下:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public static SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory) {
try (SqlSession session = sqlSessionFactory.openSession()) {
Fruit blog = session.selectOne(
"org.mybatis.example.BlogMapper.selectBlog", 101);
}
return null;
}
public static SqlSession getSqlSession2(SqlSessionFactory sqlSessionFactory){
try (SqlSession session = sqlSessionFactory.openSession()) {
FruitMapper mapper = session.getMapper(FruitMapper.class);
Fruit fruit = mapper.selectFruit(101);
}
return null;
}
}
好了,为我们继续往下看文档,发现了下面还有一段基于xml映射语句的示例,我们发现了该xml中有一条sql语句,如下所示:
我们再接着往下看,下面有两段话用来解释上述xml文件:
官方说我们在一个sql映射文件中可以声明多个sql语句,我们上面的示例代码的意思就是通过一个namespace(名称空间)指定一个xml映射文件,映射文件中定义了一条id是selectBlog的sql语句,该sql语句的传入参数是id。我们调用的方式有两种: 第一种就是通过SqlSession 对象的selectOne方法,该方法有两个参数,第一个参数是 namespace+sql语句的id组成字符串的形式,第二个参数是sql语句的执行条件参数。 第二种方式就是通过一个接口来指定查询的sql语句,接口的所在的包对应namespace,接口中的方法名对应sql映射文件中的sql语句的id ,方法的参数就是sql语句的执行条件。好了,我们仔细看一下文档中这个地方,这里描述的两种方式不就是上述提供的示例代码嘛。因此我们将这段示例的映射文件拷贝,再类路径下新建xml文件,并且将内容稍作修改如下图所示:
这里需要注意的是我们的namespance要和包名以及映射文件的路径保持一致。并且我们将示例文件中的内容改成我们自己定义的内容。好了,接着我们再回到mybatis-config.xml文件中,再mapper标签中指定一下FruitMapper.xml的路径,代码如下图所示:
这里需要注意的是如果是使用的resourcel属性的话我们需要写相对路径,即从类路径开始写。到这里基本的准备工作已经差不多了,我们将CommonsUtils类在稍作一些修该,修改之后的代码如下所示:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public static Fruit getFruit(SqlSessionFactory sqlSessionFactory) {
Fruit fruit = null;
try (SqlSession session = sqlSessionFactory.openSession()) {
fruit = session.selectOne(
"com.wcan.poet.mybatis.mapper.FruitMapper.selectFruit", 1);
}
return fruit;
}
public static Fruit getFruit2(SqlSessionFactory sqlSessionFactory){
Fruit fruit = null;
try (SqlSession session = sqlSessionFactory.openSession()) {
FruitMapper mapper = session.getMapper(FruitMapper.class);
fruit = mapper.selectFruit(1);
}
return fruit;
}
}
修改完成之后我们在测试包test下新建一个测试类MybatisTest,并且在该类中调用utils中的getFruit方法,代码如下:
public class MyBatisTest {
@Test
public void test01() throws IOException {
SqlSessionFactory sessionFactory = CommonsUtils.getSqlSessionFactory();
Fruit fruit = CommonsUtils.getFruit(sessionFactory);
System.out.println(fruit);
}
}
在该方法中,我们先获取一个SqlSessionFactory对象,再将该对象传入到getfruit方法中,最后返回一个Fruit对象。我们执行该方法,发现报出来一个异常,该异常信息如下所示:
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/wcan/poet/mybatis/mapper/FruitMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException:
Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException:
Error parsing Mapper XML. The XML location is 'com/wcan/poet/mybatis/mapper/FruitMapper.xml'.
Cause: org.apache.ibatis.builder.BuilderException: Error resolving class.
Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'Fruit'.
Cause: java.lang.ClassNotFoundException: Cannot find class: Fruit
... 35 more
上述的异常信息就是说我们在 com/wcan/poet/mybatis/mapper/FruitMapper.xml文件中定义的Fruit类找不到,这里我们在回到Fruit类中,发现resultType属性我们写了一个Fruit ,但是没有加包名,因此Mybatis在运行的时候找不到这个类,于是我们先来加上包名,将resultType属性的值改成: com.wcan.poet.mybatis.pojo.Fruit 即可,接着我们继续执行,可以发现在控制台打印出了sql语句,但是查询结果是空的。如下图所示:
好了,到这里说明我们的代码应该没有问题了,我们在数据库中先放一条数据,
接着再次执行一遍test01方法,发现还是返回的null对象,我们再来看一下映射文件中的SQL语句,查询条件是* 也就是说默认查询所有的字段,但是有一个问题,我们在程序中的变量名是驼峰命名方式,而数据库中则是采用下划线连接。两个并不是对应的,所以即使查询到了数据也无法封装到对象中,熟悉sql 的同学此时肯定想到了别名,是的,别名可以解决这个问题,我们修改sql语句,为每个字段加上别名,如下所示:
select id,fruit_name fruitName,area,fruit_color fruitColor from fruit where id = #{id}
再次运行即可查询到一条结果了:
好了,接下来我们继续调用第二种方法来查询数据,编写test02方法,代码如下:
@Test
public void test02() throws IOException {
SqlSessionFactory sessionFactory = CommonsUtils.getSqlSessionFactory();
Fruit fruit = CommonsUtils.getFruit2(sessionFactory);
System.out.println(fruit);
}
发现同样的可以查询到数据。好了,入门程序就先介绍到这里了。明天可继续给大家讲解通过注解的方式来使用Mybatis框架,小编要去睡觉了。晚安