1. 创建工程
导入坐标
打包方式:jar
导入依赖:自动导入版本 (官网下载包)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
创建数据库,导入Maven依赖
2. 创建实体类和Dao接口
IUserDao 是个人习惯
实体类实现序列化,对象映射跟数据表中字段名一致
实体类实现序列化成字节,方便传输
3. 创建Mybatis的主配置文件 SqlMapConfig.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">
配置环境、指定配置文件位置
可优化:
在environment外,使用<properties resource="配置文件"> 来导入数据库信息
如果是用注解,mapper中用class
可使用<package name="dao接口所在包"></package>来代替mapper、resource、class
4. 创建映射配置文件IUserDao.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">
添加log4j的配置文件
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# 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
否则会警告log4j不起作用
Mybatis入门案例
import com.itheima.Dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;
/**
* mybatis入门案例
*/
public class MybatisTest {
public static void main(String[] args) throws Exception {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 有连接数据库的信息
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // 有了工厂就可以生成操作对象
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
SqlSession session = factory.openSession(); // 这个对象就可以操作数据库,但是是通过Dao操作(因为只有Dao接口)
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class); // 所有创建一个代理对象(代表方法增强过了,能实现功能了)
//5.使用代理对象执行方法
List<User> users = userDao.findAll(); // 调用方法实现功能
for (User user : users) {
System.out.println(user);
}
//6.释放资源
session.close();
in.close();
}
}
注意Resource导包 --- import org.apache.ibatis.io.Resources
结果集封装
总结:
使用:映射配置文件:设置sql语句
1. 需要映射配置文件中<mapper>的namespace指明Dao接口的全类名
2. 需要<select>的id指明接口的方法
3. resultType指明结果接封装到哪个类中
4. 主配置文件中<mappers>的<mapper>的resource中指明映射配置文件的路径文件夹
Dao接口实现类是通过SqlSessionFactory工厂build读取这主配置文件的流,再openSession,再通过动态代理,getMapper传入指定Dao接口的class,得到实现类
Mybatis基于注解的入门案例:
------在Dao接口处的方法就使用注解,动态调用使用注解中的执行sql语句
把IUserDao.xml溢出,在dao接口方法上使用@Selcet注解,并且制定SQL语句,同时在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名
使用注解,来开发和编写dao实现类的方法
就不用单独在resource中创建跟接口路径对应的映射配置文件了,也不用在映射配置文件中写id=“方法名”
在主配置文件中,只需要在class中,加入Dao接口的位置
总结:
使用:注解:设置sql语句
1. 需要Dao接口中方法注解@Select("sql语句")
2. 主配置文件中<mappers>中<mapper>的class指明Dao接口全类名
Dao接口实现类是通过SqlSessionFactory工厂build读取这主配置文件的流,再openSession,再通过动态代理,getMapper传入指定Dao接口的class,得到实现类
自定义dao实现类的写法(不推荐)
通过构造方法传入工厂
在实现类的findAl方法中使用工厂得到session
写Dao实现类和使用映射配置文件的区别:
写映射配置文件:在test中获取工厂类、获取session、再动态代理获取实现类
写Dao实现类:在test中只创建实现类对象传入工厂、在实现类查找方法中获取session、用selectList(),其中参数===等同于
映射配置文件中的namespace+id
入门案例中的设计模式分析
加载配置文件:
1. 使用类加载器(只能读取类路径的配置文件) 2. ServletContext对象的fgetRealPath()
创建工厂:构建者模式
构建者模式:把 对象的创建细节隐藏,使用者直接调用方法即可拿到对象
生产SqlSession:工厂模式
工厂模式:通过工厂直接类对象,避免了类中重新new新的类,降低了类与类之间的依赖关系
创建Dao接口:代理模式
代理模式:在不修改源码的基础上,对已有的方法增强,-----没有单独创建实现类,就能使用实现类。并调用方法
6.自定义Mybatis的分析:
mybatis在使用代理dao的方式实现增删改查时,只做两件事:
动态代理:https://www.jianshu.com/p/9bcac608c714
代码实现:自定义mybatis
删除入门实例的主配置文件中的Mybatis配置
1. class Resources类
需要: 一个getResourceAsStream方法、返回值为InputStream、参数为字符串(xml文件)
导包:Resources
2. class SqlSessionFactoryBuilder
需要: 一个build方法、返回值为SqlSessionFactory、参数为流对象
3. interface SqlSessionFactory
需要: 一个openSession方法、返回值为SqlSession
4. interface SqlSession
需要: 一个getMapper方法、返回值为Dao实现类对象、参数为对应的Dao接口的字节码文件.class
填充class SqlSessionFactoryBuilder实现
首先添加工具类
自定义mybatis的配置类----Configuration---存4个连接信息
因为:loadConfiguration需要将配置文件的属性封装返回Configuration
自定义的配置类属性,需要跟配置文件对应
创建Mapper类--------存sql+封装对象
工具类中,存放Map集合的key、value存储规则定义为:方法所在全类名 . 方法名
当工厂建造者类创建工厂,读取配置文件,就会将全类名 . findAll存入Map集合
工具方法loadConfiguration中
1. SAXReader流读取配置文件,获取property节点,遍历 --------将数据库4个基本信息存入mybatis配置类
遍历得到的property标签,通过name属性的值判断,分别将对应的value值设置为配置文件对象的属性
2. 得到mappers的所有mapper标签,遍历,通过判断标签用的resource、class,来决定xml,还是注解
xml就代用loadMapperConfiguration(路径)
注解就用loadMapperAnnotation(全类名)
xml---loadMapperConfiguration(mapperPath)中参数为映射配置文件的位置
通过select得到映射配置文件根节点下的查询节点,遍历所有select,获取id、resultType、文本Sql语句
创建mapper类对象,将获取的Sql语句、resultType(需要封装全类名)存入mapper作为Map集合的值(key为namespace + id)
总结:
1. 工厂建造者方法build,传入主配置文件的流对象,使用工具类得到主配置文件的Configuration对象,返回一个工厂类,并传入Configuration对象
2. 工厂类对象使用openSession()返回一个session对象,将Configuration对象传入
动态代理:
Proxy.newProxyInstance(类加载器,class对象的所有接口,InvacationHandler(){匿名内部类---业务逻辑invoke})
该invocationHandler的匿名内部类中的invoke方法就是用来实现代理对象调用的方法,
三个参数分别是 代理对象、代理对象调用的方法被封装为对象、代理对象调用的方法时传递的实际参数
代理对象,通过这还能是对象,调用方法
总结过程:
主配置文件流传入
解析配置文件得到Configuration,返回给工厂
工厂开启session
返回session,并传入Configuration配置对象(4个信息+Map)
动态代理,返回代理对象
方法中参数创建实现类,传入Configuration的Map
构造方法得到Map,通过代理对象调用方法,invoke中的方法对象得到执行的方法名findAll+所在的接口名
selectList()方法-----执行sql语句,封装结果集
注意事项:
1. 设置Configuration的mapper,需要使用putAll方法,否则会覆盖
因此mapper就需要创建一个集合来存数据
2. 由于动态代理对象方法,返回的是T泛型,Proxy.newInstance()返回的是Object,所以需要类型转换
3. 动态代理对象方法中,的Proxy.newInstance(),参数第二个一般为被代理和代理对象共同实现的接口
用注解实现
在后面使用<package>起别名,直接指定处dao接口所在的包,就不用mapper、resource、class
/**
* 根据传入的参数,得到dao中所有被select注解标注的方法。
* 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
* @param daoClassPath
* @return
*/
private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{
//定义返回值对象
Map<String,Mapper> mappers = new HashMap<String, Mapper>();
//1.得到dao接口的字节码对象
Class daoClass = Class.forName(daoClassPath);
//2.得到dao接口中的方法数组
Method[] methods = daoClass.getMethods();
//3.遍历Method数组
for(Method method : methods){
//取出每一个方法,判断是否有select注解
boolean isAnnotated = method.isAnnotationPresent(Select.class);
if(isAnnotated){
//创建Mapper对象
Mapper mapper = new Mapper();
//取出注解的value属性值
Select selectAnno = method.getAnnotation(Select.class);
String queryString = selectAnno.value();
mapper.setQueryString(queryString);
//获取当前方法的返回值,还要求必须带有泛型信息
Type type = method.getGenericReturnType();//List<User>
//判断type是不是参数化的类型
if(type instanceof ParameterizedType){
//强转
ParameterizedType ptype = (ParameterizedType)type;
//得到参数化类型中的实际类型参数
Type[] types = ptype.getActualTypeArguments();
//取出第一个
Class domainClass = (Class)types[0];
//获取domainClass的类名
String resultType = domainClass.getName();
//给Mapper赋值
mapper.setResultType(resultType);
}
//组装key的信息
//获取方法的名称
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
String key = className+"."+methodName;
//给map赋值
mappers.put(key,mapper);
}
}
return mappers;
}
instanceof 判断 一个对象是否为一个类的实例
参数化的类型ParameterizedType 即带有泛型(参数)的某种类型 List<User>
转换为参数化类型,为了使用其方法getActualTypeArguments()得到实际类型参数
返回session时,创建连接
生成代理对象时,Proxy.newPRoxyInstance()参数中 第三个参数 new MapperProxy()传入连接
通过selectList方法供查询使用,执行CRUD操作