SSM----MyBatis 入门案例、自定义Mybatis












 






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操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值