1. Mybatis必知
简介
市面上现今用的最多的是Mybatis3.4+ 的版本,以前的Mybatis2版本的并不叫叫做ibatis ,包括现在使用mybatis导入的包很多都是经常能看到 import org.apache.ibatis.* 下的一些包,这是因为在最初的时候是由apache开源基金会 来维护,后来2010年团队转战到谷歌旗下,谷歌为区别改名为Mybatis,再后来2013年迁移到github上,现在招聘上所说熟练掌握ibatis 和Mybatis 其实都是同一个框架,并且现在想要深入了解Mybatis的源码,需要去github上找。
MyBatis 是一款优秀的持久层 框架,它支持自定义 SQL 、存储过程 以及高级映射 。MyBatis 免除了几乎所有的 JDBC 代码(底层封装JDBC) 以及设置参数和获取结果集 的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
持久层框架:操作数据库的框架
ORM:对象关系映射,Object Relation Mapping(数据库一条记录对应一个对象)
与Hibernate共性与区别
在此之前我们学习的操作数据库的方式有以下:
- 原生JDBC
- DBUtils(工具类并不是框架)
- SpringJDBCTemplate工具
- Hibernate框架(SpringDataJPA为典型实现)
- 从前处理数据库的方式如下:
特点:功能简单,sql编写在java代码中,硬编码,高耦合,维护比较复杂
- 使用框架(
Hibernate ) Hibernate:全自动ORM框架,旨在消除sql语句,通过想xml配置(或注解)javaBean和数据库表关系,框架自动生成sql语句,如下:
问题:
- sql为框架自动生成,难以sql优化
- 特殊场景需要定制sql,(可以学习Hibernate的HQL)
- 全字段
全映射问题 ,数据库字映射模糊,比如只需要查出一个字段,Hibernate会查出所有字段
- 使用框架(Mybatis)
Mybatis为半自动 ORM映射的轻量级框架,弥补Hibernate的不足,将编写sql提取出来,交给人为操作。 优点:人为操作sql,可以对sql进行优化管理,掌握sql就可以,并且层次分明,sql与java代码分开,一个专注于数据一个专注于业务。 缺点:需要对sql有一定的熟练度
2. 原生Mybatis
1.准备好maven的环境,pom.xml如下 :
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
</dependencies>
2.创建实体类,可使用lombok
package com.sang.entity;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
}
3.创建数据库表,在ssm数据库下创建student表:
create table student(
id int(11) primary key auto_increment,
name varchar(100),
email varchar(100),
age int(11)
);
4.在resources下创建maper文件编写sql语句
<?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 namespace="StudentMapper">
</mapper>
接着在mapper下编写sql语句
<?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 namespace="StudentMapper">
<select id="selectStudent" resultType="student">
select * from student where id = #{id}
</select>
</mapper>
5.配置Mybatis的config文件
<?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>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
说明
environments 下可以编写多个environments配置,作用在于开发环境,生产环境,Mysql,orcle数据库等transactionManager 配置的是事务管理器,JDBCdataSource -type 表示的是数据库连接方式,POOLED表示数据库连接池dataSource 中配置的是数据库连接的属性
6.编写测试类,在src/test/java下创建单元测试类 并创建方法和@Test
@Test
public void HelloWorld() throws IOException {
InputStream inputStream=Resources.getResourceAsStream("mybatisConfig/mybatisConfig.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = factory.openSession();
Student student= (Student)sqlSession.selectOne("StudentMapper.selectStudent", 1);
System.out.println(student);
sqlSession.close();
}
Maven下mapper文件的位置问题
这里需要说明以下几点:
- namespace是命名空间,这个mapper文件的唯一标识符
- xml文件的位置,这里直接定义在resources下,如果想放在
src/main/java下 ,则需要在maven下添加如下代码,目的在于让maven加载打包源文件下的非java文件(重点)
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
说明:
SqlSessionFactoryBuilder 根据Mybatis的config文件的流对象创建出一个sqlsession工厂对象,该对象调用openSession()方法得到sqlsession对象,sqlSession对象通过selectOne来选取方法,其中第一个参数是哪一个mapper文件的哪一个sql方法,定位标准是mapper文件的namespace的值和内部的sql方法的id值,第二个值是传递的参数,这样得到的是一个Object对象,强转成需要的对象类型,打印出来。 openSession()方法中如果传入true,则不需要自己提交事务,不写则默认需要自己提交事务,即sqlsession.commit()
3.接口式编程
将原生的方式进行大大简化 1.创建实体类的接口StudentDao(码略) 2.在resources下创建与接口名相同的文件夹 ,并在这其中创建一个mapper文件
<?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 namespace="com.sang.dao.StudentDao">
</mapper>
3.在mybatis的config文件中mappers下注册mapper文件的位置,可以用package注册某一包下所有mapper文件
<mappers>
<package name="com.sang.dao"/>
</mappers>
4.在mapper下编写sql语句
<select id="findStudentById" resultType="student">
select * from student where id=#{id}
</select>
5.在测试类中编写测试类进行测试
@Test
public void Test() throws IOException {
InputStream inputStream= Resources.getResourceAsStream("mybatisConfig/mybatisConfig.xml");
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = factoryBuilder.build(inputStream);
SqlSession sqlSession = sessionFactory.openSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.findStudentById(1);
System.out.println(student);
sqlSession.close();
}
注意:
- namespace必须指定接口的全类名
- 注册的mapper文件一定要和接口名除了扩展名外其余要一模一样!!!!
- mappers标签下,需要用package标签来注册指定包下的所有mapper文件
sqlSession.getMapper() 这里sqlsession根据接口的字节码对象来生成一个接口的代理对象,之后可以用这个代理对象来进行实现方法。
接口实现的动态代理
SqlSession sqlSession = sessionFactory.openSession();
StudentDao studentDao= sqlSession.getMapper(StudentDao.class);
Student student = studentDao.findStudentById(1);
这里 studentDao可以调用findStudentById(1) 这个方法,说明 studentDao是个对象,因为对象才具有方法行为实现啊。StudentDao是接口 ,接口是不能实例化的,更没有具体方法实现。我们并没有定义一个类,让它实现StudentDao 接口,而在这里它只是通过调用sqlSession.getMapper() 所得到的。由此,我们可以推断:肯定是sqlSession.getMapper() 方法内部产生了StudentDao 的实现类。有什么技术可以根据StudentDao 接口生成了一个实现类呢?想到这里,对于有动态代理 。
动态代理
为什么要使用动态代理?可以在不修改别代理对象代码的基础上,通过扩展代理类,进行一些功能的附加与增强。 我们先看看传统的JDK动态代理:
1.首先有一个接口
public interface Add{
void add(int i, int j);
}
2.然后是接口的实现类
public class AddImpl implements Add{
@Override
public void add(int i, int j) {
System.out.println("result = " + (i + j));
}
}
3.代理类实现InvocationHandler
public class AddProxy implements InvocationHandler {
private Object target;
public AddProxy (Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("====== 前方法() ======");
method.invoke(target, args);
System.out.println("====== 后方法() ======");
return null;
}
}
4.拿到代理对象,操作接口方法
public class test {
public static void main(String[] args) {
InvocationHandler handler = new AddProxy (new AddImpl());
Add addProxy =
(Add)Proxy.newProxyInstance(Add.class.getClassLoader(),
new Class[]{Add.class},
handler);
addProxy.add(10,20);
}
}
Proxy.newProxyInstance()方法有三个参数 :
-
类加载器(Class Loader) -
需要实现的接口数组 -
InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在。
回到我们之前的问题,我们并没有接口实现类,那没有实现类还为什么还能调用方法操作。其实是这样的:
操作数据库主要是通过SQL语句,那么只要找到SQL语句执行的方法然后执行这个方法就OK了!
Maven下mapper文件的位置问题(注意)
有的同学在接口式编程时处理mapper文件经常会碰到找不到接口方法等之类的问题,下面小子来说明一下出现的问题以及解决方案,希望能帮助到大家。 首先,说明一下Maven对resources目录下资源文件处理方式以及maven的目录结构
maven的目录结构 maven打包后classes目录结构为: resources中的资源文件会被直接放到类路径的根路径下(与类代码同级)
问题:默认情况下,会将src/main/resources 下的文件原封不动的放到target/classes 下,而src/main/java 中的非java文件不会处理
解决方案一(添加maven资源文件处理)
- 将mapper文件和接口代码放在一起如:
此时由于maven不会处理src/main/java 中的xml文件,则需要在Maven的 配置文件pomm.xml文件中添加如下配置
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
运行测试类时会出现找不到mybatisConfig文件的错误 ,这时我们打开target的classes文件夹,发下原本的resources下的资源文件并没有被maven放进来
- 默认情况下,会将
src/main/resources 下的文件原封不动的放到target/classes 下,而src/main/java 中的非java文件不会处理 - 但是,如果我们配置了
<resources> 标签对java目录下非java文件处理的配置后,Maven的默认处理方式就会被修改,从而不对src/main/resources 下资源文件进行处理!!! - 那么,
对java目录下资源进行处理,也能对resources目录下进行处理!
改善:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</build>
解决方案二(优化选择)
根据Maven的目录结构分析 我们可以将mapper文件的包结构和接口包结构设置成一样,也就是在resources目录下创建 与接口文件相同的文件夹 ,那么在生成的target目录中xml和接口文件就是在同一目录中了 如图:
4.IDEA下配置两种dtd的xml约束文件
mapper的纯净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 namespace="">
</mapper>
config的纯净xml文件
<?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>
<environments default="dev">
<environment id="dev">
<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>
</mappers>
</configuration>
在实际开发中xml的文件记不住,也不会去记他,在我们的IDEA中就提供了非常方便的方法来创建纯净的xml文件
-
首先将mapper.xml内的代码复制,然后点击左上角File->Settings->Editor->File And Code Templates -
点击左边的侧边栏上面的+号,再输入想要辨别的文件名和文件格式 -
将复制在粘贴板的代码粘贴进去,点击保存,同理config文件创建一样下面我们来右击创建一个mapper效果
注意
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
http://mybatis.org/dtd/mybatis-3-config.dtd 是该xml文件的约束文件,一般都有互联网能下载下来,IDEA按住ctrl点击进入后能看到约束内容,其中规定了该xml标签的位置顺序,如果顺序错了,IDEA会出现爆红现象
|