欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(二)Mybatis介绍

程序员文章站 2022-06-17 15:22:27
...

(一)Mybatis介绍

Hibernate与Mybatis的区别

Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。适用于需求变化不多的项目,比如后台管理系统,OA等.
Mybatis:专注于sql本身,需要程序员自己编写sql语句,sql修改,优化比较方便,虽然程序员需要自己写sql,mybatis也可以实现映射(输入映射,输出映射)
Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。

原始dao的开发方法

(1)SqlSession的使用范围

  • SqlsessionFactory创建SqlSession,使用单例模式管理工厂类
  • SqlSession是一个面向用户(程序员)接口,SqlSession中提供了很多操作数据库的方法,SqlSession是线程不安全的,所以最佳应用场合在方法体内,定义成局部变量

(2)原始dao的开发方法(程序员需要写dao接口以及实现类)

//dao接口
import java.util.List;
import com.zb.entity.Student;

public interface StudentDao {

    //通过id查询学生信息
    public Student queryStudentById(int id);
    //通过姓名模糊查询
    public List<Student> queryStudentByName(String name);
    //添加
    public int insertStudent(Student student);
    //修改
    public int updateStudent(Student student);
    //删除
    public int deletStudent(int id);
}
//dao的实现类
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.zb.entity.Student;

public class StudentDaoImpl implements StudentDao {

    //通过构造函数将我们SqlsessionFactory注入到这里
    private SqlSessionFactory factory;

    public StudentDaoImpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    //Sqlsession  是线程不安全的,他的应用场景是在方法中
    @Override
    public Student queryStudentById(int id) {
        //第一步:创建sqlsession
        SqlSession session = factory.openSession();

        Student student = session.selectOne("test.queryStudentById",id);

        //释放资源
        session.close();
        return student;
    }

    @Override
    public List<Student> queryStudentByName(String name) {
        SqlSession session = factory.openSession();

        List<Student> students = session.selectList("test.queryStudentByName", "%张%");

        session.close();
        return students;
    }

    @Override
    public int insertStudent(Student student) {
        SqlSession session = factory.openSession();

        int count = session.insert("test.addStudent",student);

        //提交事务
        session.commit();
        //释放资源
        session.close();

        return count;
    }

    @Override
    public int updateStudent(Student student) {
        SqlSession session = factory.openSession();


        int count = session.update("test.updateStudent", student);

        //提交事务
        session.commit();
        //释放资源
        session.close();

        return count;
    }

    @Override
    public int deletStudent(int id) {
        SqlSession session = factory.openSession();

        int count = session.delete("test.deleteStudentById",id);

        session.commit();
        session.close();
        return count;
    }

}

写一个工具类,创建单例的sqlSessionFactory对象

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class FactoryUtil {

    private static SqlSessionFactory factory;
    //synchronized  同步锁   同一时间只能一个线程调用这个方法   
    //线程安全  非线程安全
    public static synchronized SqlSessionFactory getFactory() throws IOException {
        if (factory == null) {
            String resource = "mybatis-config.xml";
            InputStream is = Resources.getResourceAsStream(resource);
            factory = new SqlSessionFactoryBuilder().build(is);
        }
        return factory;
    }
}

主类测试

public class Test {

    public static void main(String[] args) throws IOException {

        //------------------------Dao层的查询------------------------------
        SqlSessionFactory factory = FactoryUtil.getFactory();//先获得一个
        StudentDao studentDao = new StudentDaoImpl(factory);

        Student student = studentDao.queryStudentById(3);
        System.out.println(student);
        //---------------------------------------------------------------



        //------------------------Dao层的插入------------------------------
        SqlSessionFactory factory = FactoryUtil.getFactory();
        StudentDao studentDao = new StudentDaoImpl(factory);

        Student student2 = new Student();
        student2.setName("2222");
        student2.setAge(18);
        student2.setClass_id(2);

        System.out.println(studentDao.insertStudent(student2));
        //---------------------------------------------------------------


        //------------------------Dao层的修改(需要结合查询)------------------------------
        SqlSessionFactory factory = FactoryUtil.getFactory();
        StudentDao studentDao = new StudentDaoImpl(factory);

        Student student = studentDao.queryStudentById(10);
        student.setClass_id(21);

        System.out.println(studentDao.updateStudent(student));
        //---------------------------------------------------------------


        //------------------------Dao层的删除------------------------------
        SqlSessionFactory factory = FactoryUtil.getFactory();
        StudentDao studentDao = new StudentDaoImpl(factory);

        studentDao.deletStudent(6);
        //---------------------------------------------------------------

        }
}

(3)总结:
1、接口Dao
2、实现类Impl
3、Sqlsessionfactory单例的工具类
4、测试
缺点 :代码重复;错误无法在编译时发现
因为这些缺点,所以引出 mapper代理

Mapper代理的方法(程序员只需要写Mapper的接口,Mapper来当作接口实现类)

  1. Mapper.xml中,namespace要等于mapper接口的地址
  2. 接口中的方法名等于mapper.xml中标签statement的id
  3. Mapper接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致
  4. Mapper接口中的方法数返回值类型和mapper.xml中statement的resultType指定的类型一致
  5. Mapper.xml起名规范:实体类名(如Student)+Mapper.xml

Mapper代理的好处 :
代码不重复;错误在编译时能知道~比如:参数是object类型对象,在传统中dao开发中参数不对应检测不出来,只能在运行的时候检测出来;用了mapper之后,只要参数类型不对应,在编译的时候就能查出来,就会提示错误。

创建一个StudentMapper接口,此接口就相当于dao接口

public interface StudentMapper {

    //通过id查询学生信息
    public Student queryStudentById(int id);
    //通过姓名模糊查询
    public List<Student> queryStudentByName(String name);
    //添加
    public int addStudent(Student student);
    //修改
    public int updateStudent(Student student);
    //删除
    public int deletStudentById(int id);    
}

编写StudentMapper.xml
需要注意的地方,图片中已表明。
(二)Mybatis介绍

主类测试

public class Test {

    public static void main(String[] args){
        //使用mapper代理的方式开发
        SqlSessionFactory sessionFactory = FactoryUtil.getFactory();
        //开启session
        SqlSession session = sessionFactory.openSession();
        //生成mapper接口实现类的代理对象
        StudentMapper mapper = session.getMapper(StudentMapper.class);

        //-------------------查询-----------------------
        Student student = mapper.queryStudentById(2);
        System.out.println(student);
        //-------------------增加-----------------------
        Student student = new Student();
        student.setName("啊啊");
        student.setAge(18);
        student.setClass_id(2);
        mapper.addStudent(student);
        session.commit();
        session.close();
}

Mapper代理对象内部调用selectOne或者SelectList

1) 如果mapper方法返回的是单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库
2) 如果mapper方法返回的集合对象,代理对象内部通过SelectList查询数据库

Mapper接口方法参数只能有一个是否影响系统开发?

mapper接口只能有一个参数,因为在xml中单个标签只能有一个parameterType,所以就限定了方法中的参数中能有一个。
解决办法:就是使用包装类,即包装类的属性包括其他类的对象或其他类型的数据。包装类其实就是给原来的类增添一些属性,如现有一个学生类,属性有姓名name,id,班级class_id,年龄age等,那么创建一个新的类叫做Studentgrade,它继承了student,额外的属性有分数等等。

Mapper 是如何实现的不用实现类的?

通过namespace 反射 得到 接口 ,通过接口中的方法名去找标签中的id,找到了对应的id,就去执行标签中的操作。根据返回类型 是pojo 还是集合 来决定是用selectOne还是selectList。

Mybatis-config.xml文件

对于java项目,在Mybatis-config.xml中必须要配置的有2个
(1) 数据源
(2)加载xml文件

(1)数据源

<properties resource="db.properties"></properties>
<environments default="development">  
        <environment id="development"> 
         <!-- 使用jdbc事务管理,事务由mybatis控制 -->  
            <transactionManager type="JDBC" />  
         <!-- 数据库连接池,由mybatis管理 --> 
            <dataSource type="POOLED">  
                <property name="driver" value="${jdbc.driverClass}" />  
                <property name="url" value="${jdbc.url}" />  
                <property name="username" value="${jdbc.username}" />  
                <property name="password" value="${jdbc.password}" />  
            </dataSource>  
        </environment>  
    </environments>   

注意:
如果不加第一句话,那么你必须把内容写在mybatis-config.xml中,建议还是加上第一句话,即把数据库连接放在properties里,这样体现了功能分离的特点,以后修改也方便。
附上properties文件:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/数据库名?characterEncoding=UTF-8
jdbc.username=填写自己的账号名
jdbc.password=填写自己的密码

(2)加载xml文件
有两种方式:

  • 第一种就是非Mapper开发
  • 第二种就是Mapper开发
<mappers>
    <!-- 第一种 填写相对路径 非Mapper开发的xml的名字没有要求-->
    <mapper resource="student.xml" />
    <!-- 第二种:
        使用规范:
        1、mapper接口的名字要与mapper.xml文件的名字保持一致,并且在同一个文件夹中
        2、必须使用mapper代理的方式开发!
    -->
    <mapper class="com.zb.Mapper.StudentMapper"/>
</mappers>

Mybatis-config.xml文件中还可以写的内容
(1)定义别名
意思就是:在放sql语句的xml中,parameterType和resultType需要写全路径名,有了别名之后就可以直接写别名。

定义别名分2种:

  • 为某个类单独定义别名
  • 为包底下的所以的类定义别名,即批量定义别名
<!-- 定义别名 -->
<typeAliases>
        <!-- 单独定义别名 -->
        <!-- <typeAlias type="com.zb.entity.Student" alias="student"/> -->

        <!-- 批量定义别名 -->
        <!-- package 是扫描的包的名字 
            自动扫描指定的包,为包下的类去自动设置别名,别名就是类名  大小写无所谓
        -->
        <package name="com.zb.entity"/>
</typeAliases>

补充:
其实int,double,String在放sql语句的xml中也需要写全路径名,但是它们是mybatis默认支持的别名。(mybatis默认支持的别名如下图)
(二)Mybatis介绍