软件开发的一套解决方案,不同的框架用于解决不同的问题,框架中封装了许多细节,开发者可以使用极为简单的方式来实现功能,大大提高了开发效率。
JDBC技术:
Connection
PreparedStatement
ResultSet
Spring的JdbcTemplate:Spring对JDBC的简单封装
Apache的DBUtils:类似于JdbcTemplate
以上这些都不是框架,仅仅是工具类。
是一个基于java的持久层框架,内部封装了JDBC,使开发者只需要关注sql语句本身,而不需要花费去处理加载驱动,创建连接,创建Statement对象等繁杂的过程。 通过XML或者注解的方式将要执行的各种Statement进行配置,通过java对象和Statement的动态参数进行映射执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
采用ORM思想实现了结果集的封装:
ORM Object Relational Mapping 对象关系映射
将数据库表和实体类以及实体类的属性对应起来,通过操作实体类来实现操作数据库表。
User类 | User表 |
---|---|
id | id |
user_name | user_name |
保证实体类中的属性和数据库表的字段名称一致。
Maven home path为安装好的maven根路径,User settings file为修改maven文件夹中的settings.xml的路径,Local rrepository为本地的maven文件夹根路径
在src/main/java中创建package:com.huzhen.domain,在该包下创建实体类user,以及创建dao包,在下面创建user interface接口,定义需要实现的抽象方法;
在src/main/resources中创建SqlMapConfig.xml映射文件:mysql环境,事务类型,数据源,以及4个数据库基本信息, 指定映射配置文件的位置;
在resources中创建com.huzhen.dao创建UserDaoImpl.xml:
select id即为user interface的抽象方法
具体代码如下:
/*
直接使用下面的动态代理来执行sql语句,会发生错误:A query was run and no Result Maps were found for the Mapped Statement 'com.huzhen.domain.dao.UserInterface.findAll'.
It's likely that neither a Result Type nor a Result Map was specified.
原因在于:查询语句的返回值需要与指定的实体类进行对应,所以需要配置resultType
*/
SqlSession session = null;
InputStream is = null;
// 创建工厂实现类的细节忽略,而使用SqlSessionFactoryBuilder类来创建SqlSession对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try {
// 这里使用的是相对路径来读取配置文件,一般不采用这种
is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = builder.build(is);
// 使用工厂生产SqlSession对象
session = factory.openSession();
// 使用SqlSession来创建Dao接口的的代理对象
UserInterface userDao = session.getMapper(UserInterface.class);
// 使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(session != null){
session.close();
}
try {
if(is != null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
而基于注解来替换Dao接口的配置文件:
将UserDao.xml移除,在dao接口的方法findAll上使用@Select注解,并且指定sql语句,同时需要在SqlMapConfig.xml中的mapper配置处,使用class属性来指定Dao接口的全限定类名。
在实际开发中,均不采用dao实现类的方式,无论使用XML还是注解进行配置,但是Mybatis是支持写dao实现类的
拥有数据库的信息,可以创建Connection对象;
拥有mapper即拥有映射配置信息;
拥有sql语句,可以获取PreparedStatement来执行Sql语句,以及查询集结果封装的类型
读取配置文件,用到解析XML技术,dom4j来解析xml文件
(selectList method)
1) 根据配置文件信息创建Connection对象:注册确驱动,获取连接
2) 获取预处理对象PreparedStatement,获取sql语句并且执行
3) 执行查询,返回一个ResultSet对象:ResultSet res = preparedStatement.executeQuery();
4) 遍历结果集用于封装
List<E> list = new ArrayList<>();
while(res.next()){
E element = xxxx;
// 反射来实现结果集的对象封装
(E element = (E) Class.forName(“全限定类名”).newInstance();)
list.add(element); }
element的实例化: 使用resultType中的全限定类名来反射得到一个user对象
封装:将res的每个内容都添加到element中,实体类属性与表中的列名是一致的,于是可以将表的列名看成是实体类的属性名,可以使用反射的方式根据名称来获取每个属性并赋值。
关于映射信息:包含两个部分----执行的sql语句和封装结果集的全限定类名,将这两个信息组成定义成一个对象。
使用map来存储:key存储nameSpace以及select id,value存储String sql以及String domainClassPath
Key | Value |
---|---|
nameSpace | String sql |
select id | String domainClassPath |
根据Dao接口的字节码文件来创建dao的代理对象
Public <T> T getMapper(Class<T> daoInterfaceClass){
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
}
注意:
使用mybatis能做什么? 创建代理对象和在代理对象中调用selectList方法
有哪些类和接口需要关注?
具体流程为:
io中的Resources方法读取配置文件得到InputStream信息
交给构建者SqlSessionFactoryBuilder,使用工具类构建了一个工厂对象
工厂对象调用其openSession方法,创建代理对象
ClassLoader类加载器:使用的和被代理对象是相同的类加载器,代理的接口是谁,
就使用其getClassLoader的类加载器
Class[] interfaces代理对象要实现的接口:和被代理对象实现相同的接口
InvocationHandler如何代理:增强的方法,需要自己提供,此处是一个InvocationHeadler接口,
需要写一个接口的实现类,在实现类中调用selectList方法
在session方法里面去实现创建代理对象和查询操作
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接口中的方法数组,由于dao接口下可能存在多个方法,因此返回的是一个Method数组
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
//判断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;
}