【MyBatis】MyBatis的介绍和基本使用

10 篇文章 2 订阅
6 篇文章 0 订阅

目录

一、数据库操作框架的历程

1.1 JDBC

1.2 DBUtils

1.3 Hibernate

1.4 Spring JDBC:JDBCTemplate

1.5 Spring Data JPA

二、什么是MyBatis?

2.1 传统JDBC与MyBatis相比的弊病

2.2 MyBatis中的组件

2.3 MyBatis的体系结构

三、快速搭建MyBatis项目

 1、创建普通的maven项目

 2、导入相关的依赖

 3、创建对应的数据表

 4、创建与表对应的实体类对象

5、创建对应的Mapper接口

6、编写配置文件

 7、编写测试类

四、增删改查的基本操作

4.1 通过xml实现mapper

4.2 使用注解实现mapper


一、数据库操作框架的历程

1.1 JDBC

JDBC(Java Data Base Connection,java数据库连接)是一种用于执 行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

  • 优点:运行期:快捷、高效
  • 缺点:编辑期:代码量大、繁琐异常处理、不支持数据库跨平台

jdbc核心api:

  1. DriverManager 连接数据库
  2. Connection 连接数据库的抽象
  3. Statment 执行SQL
  4. ResultSet 数据结果集

JDBC这种方式的代码流程一般是:1、加载数据库驱动,2、创建数据库连接对象,3、创建 SQL 执行语句对象PreparedStatement,4、执行 SQL得到ResultSet,5、处理ResultSet 结果集。它的过程比较固定,下面我们再手写一遍 JDBC 代码,回忆一下初学 Java 的场景。

public class JdbcTest {
    public void testJdbc() {
        // 在URL中配置了MYSQL信息,其中包括数据库连接用户名和密码等配置信息
        String url = "jdbc:mysql://localhost:3306/myblog?user=root&password=1234&useUnicode=true&characterEncoding=UTF8&useSSL=false";
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 通过JDBC驱动,获取数据库连接
            conn = DriverManager.getConnection(url);
            String author = "coolblog.xyz";
            String date = "2018.06.10";
            String sql = "SELECT id, title, author, content, create_time FROM article WHERE author = '" + author + "' AND create_time > '" + date + "'";
            
            // 使用conn创建Statement对象,用于执行SQL
            Statement stmt = conn.createStatement();
            // ResultSet用于接收执行SQL的结果    stmt用于执行SQL
            ResultSet rs = stmt.executeQuery(sql);
            // 通过rs将查询出来的结果转移到JAVA类中,这一步需要我们手动填充
            List<Article> articles = new ArrayList<>(rs.getRow());
            while (rs.next()) {
                Article article = new Article();
                article.setId(rs.getInt("id"));
                article.setTitle(rs.getString("title"));
                article.setAuthor(rs.getString("author"));
                article.setContent(rs.getString("content"));
                article.setCreateTime(rs.getDate("create_time"));
                articles.add(article);
            }
            System.out.println("Query SQL ==> " + sql);
            System.out.println("Query Result: ");
            articles.forEach(System.out::println);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                // 使用完要关闭连接
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

代码比较简单,就不多说了。下面来看一下测试结果:

上面代码的步骤比较多,但核心步骤只有两部,分别是执行 SQL 和处理查询结果。从开发人员的角度来说,我们也只关心这两个步骤。如果每次为了执行某个 SQL 都要写很多额外的代码。比如打开驱动,创建数据库连接,就显得很繁琐了。当然我们可以将这些额外的步骤封装起来,这样每次调用封装好的方法即可。这样确实可以解决代码繁琐,冗余的问题。不过,使用 JDBC 并非仅会导致代码繁琐,冗余的问题。在上面的代码中,我们通过字符串对 SQL 进行拼接。这样做会导致两个问题,第一是拼接 SQL 可能会导致 SQL 出错,比如少了个逗号或者多了个单引号等。第二是将 SQL 写在代码中,如果要改动 SQL,就需要到代码中进行更改。这样做是不合适的,因为改动 Java 代码就需要重新编译 Java 文件,然后再打包发布。同时,将 SQL 和 Java 代码混在一起,会降低代码的可读性,不利于维护。关于拼接 SQL,是有相应的处理方法。比如可以使用 PreparedStatement,同时还可解决 SQL 注入的问题。

除了上面所说的问题,直接使用 JDBC 访问数据库还会有什么问题呢?这次我们将目光转移到执行结果的处理逻辑上。从上面的代码中可以看出,我们需要手动从 ResultSet 中取出数据,然后再设置到 Article 对象中。好在我们的 Article 属性不多,所以这样做看起来也没什么。假如 Article 对象有几十个属性,再用上面的方式接收查询结果,会非常的麻烦。而且可能还会因为属性太多,导致忘记设置某些属性。以上的代码还有一个问题,用户需要自行处理受检异常,这也是导致代码繁琐的一个原因。哦,还有一个问题,差点忘了。用户还需要手动管理数据库连接,开始要手动获取数据库连接。使用好后,又要手动关闭数据库连接。不得不说,真麻烦。

没想到直接使用 JDBC 访问数据库会有这么多的问题。如果在生产环境直接使用 JDBC,怕是要被 Leader 打死了。当然,视情况而定。如果项目非常小,且对数据库依赖比较低。直接使用 JDBC 也很方便,不用像 MyBatis 那样搞一堆配置了。

MyBatis VS JDBC

首先我们来看看MyBatis访问数据库的过程:

  1. 读取配置文件
  2. 创建SqlSessionFactoryBuilder对象
  3. 通过SqlSessionFactoryBuilder创建SqlSessionFactory对象
  4. 通过SqlSessionFactory创建SqlSession
  5. 为Dao 接口生成代理类
  6. 调用接口方法访问数据库

需要注意的是,在MyBatis中SqlSessionFactoryBuilder 和 SqlSessionFactory 以及 SqlSession 等对象的作用域和生命周期是不一样的。

  • SqlSessionFactoryBuilder
    • 这个类可以被实例化,使用和丢弃,一旦创建了SqlSessionFactory,就不需要它了,所以,SqlSessionFactoryBuilder实例的最佳作用域是方法作用域(也就是局部方法变量)
  • SqlSessionFactory
    • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另外一个实例,使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次,多次重建SqlSessionFactory被视为一种代码”坏味道“。因此SqlSessionFactory的最佳作用域是应用作用域,有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
  • SqlSession
    • 每个线程都应该有它自己的SqlSession实例,SqlSession的实例不是线程安全的,因此是不能被共享的,所有它的最佳的作用域是请求或方法作用域,绝对不能将SqlSession实例的引用放在了一个类的静态域,比如Servlet框架中的HttpSession中。
  • 映射器实例
    • 映射器是一些由你创建的,绑定你映射的语句的接口,映射器接口的实例是从SqlSession中获得的,因此从技术层面讲,任何映射器实例的最大作用域是请求他们的的SqlSession相同的,尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。并不需要显示地关闭映射器实例。

总的来说,MyBatis在易用性上要比JDBC好太多,不过JDBC与MyBatis的目标不同,JDBC是作为一种基础服务,而MyBatis则是构建在基础服务之上的框架。所以JDBC的流程繁琐,从JDBC的角度来说,这里的每一个步骤对于完成数据访问请求来说都是必须的。

1.2 DBUtils

DBUtils是Java编程中的数据库操作实用工具,小巧简单实用。DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。

DBUtils三个核心功能介绍:

  1.  QueryRunner中提供对sql语句操作的API
  2.  ResultSetHandler接口,用于定义select操作后,怎样封装结果集
  3.  DBUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法

1.3 Hibernate

Hibernate 是由 Gavin King 于 2001 年创建的开放源代码的对象关系框架。它强大且高效的构建具有关系对象持久性和查询服务的 Java 应用程序。

Hibernate 将 Java 类映射到数据库表中,从 Java 数据类型中映射到 SQL 数据类型中,并把开发人员从 95% 的公共数据持续性编程工作中解放出来。

Hibernate 是传统 Java 对象和数据库服务器之间的桥梁,用来处理基于 O/R 映射机制和模式的那些对象。它是一种全自动的ORM框架 。

ORM(对象关系映射):

  • O:object java对象
  • R:relational 关系型数据
  • M:mapping 映射

Hibernate 的使用:

首先,在POM文件中添加所需依赖

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.2.Final</version>
</dependency>

接着进行环境配置,主要是关于数据库方面的配置。

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mybatisdemo</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">admin</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.show_sql">true</property>
        <mapping resource="chapter1/xml/Student.hbm.xml" />
    </session-factory>
</hibernate-configuration>

环境配置完成之后,我们接着编写映射文件,将表字段与实体类的属性关联起来。如下Student.hbm.xml

<hibernate-mapping package="com.jay.entity">
    <class table="student" name="Student">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <property name="age" column="age"/>
        <property name="classId" column="class_id"/>
    </class>
</hibernate-mapping>

所有配置完成之后,我们就可以开始编写测试代码进行测试:

public class HibernateTest {
    private SessionFactory sessionFactory;

    @Before
    public void init() {
        Configuration configuration = new Configuration();
        configuration.configure("chapter1/hibernate.cfg.xml");
        sessionFactory = configuration.buildSessionFactory();
    }
    @After
    public void destroy() {
        sessionFactory.close();
    }
    @Test
    public void testORM() {
        System.out.println("--------------ORM Query-------------");
        Session session = null;
        try {
            session = sessionFactory.openSession();
            int id = 1;
            Student student = session.get(Student.class, id);
            System.out.println("ORM Query Result:");
            System.out.println(student.toString());
            System.out.println();
        } finally {
            if (Objects.nonNull(session)) {
                session.close();
            }
        }
    }
    @Test
    public void testHQL() {
        System.out.println("--------------HQL Query-----------");
        Session session = null;
        try {
            session = sessionFactory.openSession();
            String hql = "FROM Student WHERE name=:name";
            Query query = session.createQuery(hql);
            query.setParameter("name", "点点");
            List<Student> studentList = query.list();
            System.out.println("HQL Query Result:");
            studentList.forEach(System.out::println);
            System.out.println();
        } finally {
            if (Objects.nonNull(session)) {
                session.close();
            }
        }
    }
   @Test
    public void testJpaCriteria() {
       System.out.println("-------------JPA Criteria-------------");
       Session session = null;
       try {
            session = sessionFactory.openSession();
            CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
            CriteriaQuery<Student> criteriaQuery = criteriaBuilder.createQuery(Student.class);
            // 定义FROM子句
            Root<Student> student = criteriaQuery.from(Student.class);
            // 构建查询条件
            Predicate equal = criteriaBuilder.equal(student.get("name"), "点点");
            // 通过具有语义化的方法构建SQL,等价于SELECT ... FROM student WHERE ...
            criteriaQuery.select(student).where(equal);
            Query<Student> query = session.createQuery(criteriaQuery);
            List<Student> studentList = query.getResultList();
            System.out.println("JPA Criteria Query Result:");
            studentList.forEach(System.out::println);
       } finally {
           if (Objects.nonNull(session)) {
               session.close();
           }
       }
   }
}

如上代码清单所示,我编写了三个测试用例,第一个直接使用Hibernate生成SQL的功能,如果查询比较简单可以采用此种方式,生成的SQL是

select student0_.id as id1_0_0_, student0_.name as name2_0_0_, student0_.age as age3_0_0_, student0_.class_id as class_id4_0_0_ from student student0_ where student0_.id=?

第二个测试用例,我编写了一条HQL语句,并通过Query来设置参数,同样Hibernate在运行时会将HQL转化成对应的SQL,转化后的SQL如下:

select student0_.id as id1_0_, student0_.name as name2_0_, student0_.age as age3_0_, student0_.class_id as class_id4_0_ from student student0_ where student0_.name=?

第三个测试用例,我们使用JPA Criteria 进行查询,JPA Criteria 具有类型安全,面向对象和语义化的特点,使用JPA Criteria,我们可以用写Java 代码的方式进行数据库操作,无需手写SQL,第三个用例和第二个用例进行的是同样的查询,所以生成的SQL区别不大。

测试代码的运行结果:

Hibernate 优势

  • Hibernate 使用 XML 文件来处理映射 Java 类别到数据库表格中,并且不用编写任何代码。
  • 为在数据库中直接储存和检索 Java 对象提供简单的 APIs。
  • 如果在数据库中或任何其它表格中出现变化,那么仅需要改变 XML 文件属性。
  • 抽象不熟悉的 SQL 类型,并为我们提供工作中所熟悉的 Java 对象。
  • Hibernate 不需要应用程序服务器来操作。
  • 操控你数据库中对象复杂的关联。
  • 最小化与访问数据库的智能提取策略。
  • 提供简单的数据询问。

 

Hibernate劣势

  • hibernate的完全封装导致无法使用数据的一些功能。
  • Hibernate的缓存问题。
  • Hibernate对于代码的耦合度太高。
  • Hibernate寻找bug困难。
  • Hibernate批量数据操作需要大量的内存空间而且执行过程中需要的对象太多

MyBatis VS Hibernate

  1. MyBatis 需要使用者自行维护SQL,灵活性高,方便对sql进行优化,Hibernate 可以自动生成SQL,使用成本小。
  2. MyBatis 适合于需求变动频繁,业务量的系统,Hibernate 更加适合于变动比较小的系统,比如OA系统

1.4 Spring JDBCJDBCTemplate

Spring JDBC是在JDBC上面做的一层比较薄的封装,构造了JdbcTemplate,主要是为了解决直接使用JDBC的一些痛点,易用性得到了不少的提升。

JdbcTemplate针对数据查询提供了多个重载的模板方法,你可以根据需要选用不同的模板方法。如果你的查询很简单,仅仅是传入相应SQL或者相关参数,然后取得一个单一的结果,那么你可以选择如下一组便利的模板方法。

  • 优点:运行期:高效、内嵌Spring框架中、支持基于AOP的声明式事务 ​
  • 缺点:必须于Spring框架结合在一起使用、不支持数据库跨平台、默认没有缓存

JdbcTemplate的使用:

引入的依赖

<properties>
    <spring.version>4.3.17.RELEASE</spring.version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>

在使用Spring JDBC之前我们需要做一些配置,我们新建一个配置文件,命名为application.xml,在此配置文件中,我们配置了数据库的连接信息dataSource,注册了JdbcTemplate实例。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder  location="jdbc.properties"/>
    <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
        <!-- 配置与数据库交互的4个必要属性 -->
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

配置完成之后,我们可以写一个测试类来测试一下。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:chapter1/application.xml")
public class SpringJdbcTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void  testSpringJdbc() {
        String sql = "select id,name,age from student where name LIKE ?";

        List<Student> studentList = jdbcTemplate.query(sql, new Object[]{"%点点%"}, new BeanPropertyRowMapper<Student>(Student.class));
        System.out.println("----->执行的sql={}"+sql);
        System.out.println("----->查询结果={}"+studentList.get(0).toString());
    }
}

运行结果

从上面的测试代码我们可以看出,相对于原生JDBC,Spring JDBC 易用性大大提升,注入jdbcTemplate之后,我们就可以通过jdbcTemplate来操作,只关注sql的执行以及结果的处理即可。代码简化了很多。但是SQL语句仍然写在代码中。

1.5 Spring Data JPA

首先引入依赖

<properties>
    <spring.version>4.3.17.RELEASE</spring.version>
</properties>
<!--///Spring JPA-->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.11.8.RELEASE</version>
</dependency>

接着添加配置文件application-jpa.xml,主要是配置数据库连接信息,以及事务相关的信息

<!--启用注解配置和包扫描-->
<context:annotation-config/>
<context:component-scan base-package="com.jay"/>

<!--创建Spring Data JPA实例对象-->
<jpa:repositories base-package="com.jay.chapter1"/>
<context:property-placeholder  location="jdbc.properties"/>
<bean id="dataSource"
        class="org.apache.ibatis.datasource.pooled.PooledDataSource">
    <!-- 配置与数据库交互的4个必要属性 -->
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="com.jay.entity"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="true"/>
            <property name="showSql" value="true"/>
        </bean>
    </property>
</bean>
<!--事务管理器-->
<bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--事务管理-->
<tx:advice id="transactionAdvice"
            transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="daoPointCut" expression="execution(* com.jay.chapter1.mapper.*.*(..))"/>
    <aop:advisor advice-ref="transactionAdvice" pointcut-ref="daoPointCut"/>
</aop:config>

配置文件添加完成之后,接着我们编写一个接口继承CrudRepository接口,使其具备基本的增删改查功能。

public interface JpaStudentDao extends CrudRepository<JpaStudent,Integer>{
    /**
     * @param name
     * @return
     */
    List<JpaStudent> getByNameLike(String name);
}

 

DAO接口添加完成之后,接着我们添加一个测试类进行测试。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:chapter1/application-jpa.xml")
public class JPATest {
    @Autowired
    JpaStudentDao jpaStudentDao;

    @Before
   public void init() {
        JpaStudent jpaStudent = new JpaStudent("张三", 12, "121");
        jpaStudentDao.save(jpaStudent);
    }

    @Test
    public void testCrudRepostitory() {
        List<JpaStudent> jpaStudents = jpaStudentDao.getByNameLike("张三");
        jpaStudents.forEach(System.out::println);
        System.out.println();
    }
}

如上测试类所示,我先使用了JPA自带的save方法向数据库中插入了一条数据,接着自定义了一个查询方法。JPA中查询方法可以由我们声明的命名查询生成,也可以由方法名解析。方法名以find…By, read…By, query…By, count…By和 get…By做开头。在By之前可以添加Distinct表示查找不重复数据。By之后是真正的查询条件。

可以查询某个属性,也可以使用条件进行比较复杂的查询,例如Between, LessThan, GreaterThan, Like,And,Or等。

字符串属性后面可以跟IgnoreCase表示不区分大小写,也可以后跟AllIgnoreCase表示所有属性都不区分大小写。

可以使用OrderBy对结果进行升序或降序排序。

可以查询属性的属性,直接将几个属性连着写即可,如果可能出现歧义属性,可以使用下划线分隔多个属性。

运行结果如下:

MyBatis VS JPA

通过上面的实例,我们可以了解到JPA的使用,JPA类似于Hibernate都可以自动生成SQL,不同之处是,JPA还可以根据方法名来解析生成sql。MyBatis 还是需要使用者自行维护sql。

二、什么是MyBatis

Mybatis前身是iBatis,其源于“Internet”和“ibatis”的组合,MyBatis 是一款优秀的持久层半自动的ORM框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。但是MyBatis的自动化程度不高,移植性也不高,有时从一个数据库迁移到另外一个数据库的时候需要自己修改配置,所以称只为半自动ORM框架。

2.1 传统JDBCMyBatis相比的弊病

传统JDBC

@Test
public void test() throws SQLException {
    Connection conn=null;
    PreparedStatement pstmt=null;
    try {
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.创建连接
        conn= DriverManager.
                getConnection("jdbc:mysql://localhost:3306/mybatis_example", "root", "123456");

        // SQL语句
        String sql="select id,user_name,create_time from t_user where id=?";
        // 获得sql执行者
        pstmt=conn.prepareStatement(sql);
        pstmt.setInt(1,1);
        // 执行查询
        pstmt.execute();
        ResultSet rs= pstmt.getResultSet();
        rs.next();
        User user =new User();
        user.setId(rs.getLong("id"));
        user.setUserName(rs.getString("user_name"));
        user.setCreateTime(rs.getDate("create_time"));
        System.out.println(user.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
    finally{
        // 关闭资源
        try {
            if(conn!=null){
                conn.close();
            }
            if(pstmt!=null){
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

传统JDBC的问题如下:

  1. 数据库连接创建,释放频繁造成西戎资源的浪费,从而影响系统性能,使用数据库连接池可以解决问题。
  2. sql语句在代码中硬编码,造成代码的不已维护,实际应用中sql的变化可能较大,sql代码和java代码没有分离开来维护不方便。
  3. 使用preparedStatement向有占位符传递参数存在硬编码问题因为sql中的where子句的条件不确定,同样是修改不方便/
  4. 对结果集中解析存在硬编码问题,sql的变化导致解析代码的变化,系统维护不方便。

MyBatis对传统的JDBC的解决方案:

  1. 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
    • 解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。
  2. Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
    • 解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
  3. 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
    • 解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
  4. 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
    • 解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

一个Mybatis最简单的使用列子如下:

public class App {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        Reader reader;
        try {
            // 将XML配置文件构建为Configuration配置类
            reader = Resources.getResourceAsReader(resource);
            // 通过加载配置文件流构建一个SqlSessionFactory  DefaultSqlSessionFactory
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
            // 通过SqlSessionFactory获取数据源执行器  DefaultSqlSession
            SqlSession session = sqlMapper.openSession();
            try {
                // 执行查询 底层执行jdbc
                //User user = (User)session.selectOne("com.tuling.mapper.selectById", 1);
                // 通过mapper对象执行sql
                UserMapper mapper = session.getMapper(UserMapper.class);
                System.out.println(mapper.getClass());
                User user = mapper.selectById(1L);
                System.out.println(user.getUserName());
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                session.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

总结下就是分为下面四个步骤:

  1. 从配置文件(通常是XML文件)得到SessionFactory;
  2. 从SessionFactory得到SqlSession;
  3. 通过SqlSession进行CRUD和事务的操作;
  4. 执行完相关操作之后关闭Session。

2.2 MyBatis中的组件

通过上面的讲解,我们就知道了MyBatis中有下面四个重要的组件:

  • SqlSessionFactoryBuilder读取配置信息创建SqlSessionFactory,建造者模式,方法级别生命周期;
  • SqlSessionFactory创建Sqlsession,工厂单例模式,存在于程序的整个生命周期;
  • SqlSession代表一次数据库连接,一般通过调用Mapper访问数据库,也可以直接发送SQL执行;线程不安全,要保证线程独享(方法级);
  • SQL Mapper由一个Java接口和XML文件组成,包含了要执行的SQL语句和结果集映射规则。方法级别生命周期;

2.3 MyBatis的体系结构

MyBatis是一个半自动的ORM框架,除了需要编写POJO和映射关系之外,还需要编写SQL语句。MyBatis映射文件三要素:

  • SQL
  • 映射规则
  • POJO

优点:

  • 与JDBC相比,减少了50%的代码量
  • 最简单的持久化框架,简单易学
  • SQL代码从程序代码中彻底分离出来,可以重用
  • 提供XML标签,支持编写动态SQL
  • 提供映射标签,支持对象与数据库的ORM字段关系映射
  • 支持缓存、连接池、数据库移植....

 缺点:

  •  SQL语句编写工作量大,熟练度要高
  •  数据库移植性比较差,如果需要切换数据库的话,SQL语句会有很大的差异

Mybaits整体体系图

三、快速搭建MyBatis项目

 1、创建普通的maven项目

 2、导入相关的依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.tulingxueyuan</groupId>
    <artifactId>mybatis_helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency> 
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>  
    </dependencies>
</project>

 

驱动请按照数据库版本进行对应 MySQL :: MySQL Connector/J 8.1 Release Notes

 3、创建对应的数据表

 4、创建与表对应的实体类对象

emp.java

package cn.tulingxueyuan.pojo;
public class Emp {
    private Integer id;
    private String username;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

5、创建对应的Mapper接口

其实这个所谓的Mapper接口,就是我们编写的dao层接口,两个是一样的,叫mapper或者dao都可以,总之这个接口就是要和mapper.xml编写的各种查询sql对应上即可。

EmpMapper.java

package cn.tulingxueyuan.mapper;
import cn.tulingxueyuan.pojo.Emp;
import org.apache.ibatis.annotations.Select;

public interface EmpMapper {
    // 根据id查询Emp实体
    //@Select("select * from emp where id=#{id}")
    Emp selectEmp(Integer id);
    // 插入
    Integer insertEmp(Emp emp);
    // 更新
    Integer updateEmp(Emp emp);
    // 删除
    Integer deleteEmp(Integer id);
}

6、编写配置文件

mybatis-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="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 配置数据库连接 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="EmpMapper.xml"/>-->
        <!-- 指定mapper类 -->
        <mapper class="cn.tulingxueyuan.mapper.EmpMapper"></mapper>
    </mappers>
</configuration>

EmpMapper.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类 -->
<mapper namespace="cn.tulingxueyuan.mapper.EmpMapper">
    <!--根据id查询Emp实体-->
    <select id="selectEmp" resultType="cn.tulingxueyuan.pojo.Emp">
        select * from Emp where id = #{id}
    </select>
    <insert id="insertEmp">
        INSERT INTO
        `mybatis`.`emp` ( `username`)
        VALUES (#{username});
    </insert>
    <update id="updateEmp">
        UPDATE EMP
        SET username=#{username}
        WHERE id=#{id}
    </update>
    <delete id="deleteEmp">
        DELETE FROM emp
        WHERE id=#{id}
    </delete>
</mapper>

 7、编写测试类

MyTest.java

/***
 * MyBatis 搭建步骤:
 * 1.添加pom依赖 (mybatis的核心jar包和数据库版本对应版本的驱动jar包)
 * 2.新建数据库和表
 * 3.添加mybatis全局配置文件 (可以从官网中复制)
 * 4.修改mybatis全局配置文件中的 数据源配置信息
 * 5.添加数据库表对应的POJO对象(相当于我们以前的实体类)
 * 6.添加对应的PojoMapper.xml (里面就维护所有的sql)
 *      修改namespace:  如果是StatementId没有特殊的要求
 *                      如果是接口绑定的方式必须等于接口的完整限定名
 *      修改对应的id(唯一)、resultType 对应返回的类型如果是POJO需要制定完整限定名
 * 7.修改mybatis全局配置文件:修改Mapper
 */
public class MybatisTest {
    SqlSessionFactory sqlSessionFactory;

    @Before
    public void before(){
        // 从 XML 中构建 SqlSessionFactory
        String resource = "mybatis.xml";
        InputStream inputStream = null;
        try {
                       // 通过Resources得到XML配置文件输入流
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
		 // 通过SqlSessionFactoryBuilder,将配置文件输入流转换城sqlSessionFactory 
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    /**
     * 基于StatementId的方式去执行SQL
     *      <mapper resource="EmpMapper.xml"/>
     * @throws IOException
     */
    @Test
    public void test01() {
        // 通过sqlSessionFactory获取sqlSession去执行sql
        try (SqlSession session = sqlSessionFactory.openSession()) {
            Emp emp = (Emp) session.selectOne("cn.tulingxueyuan.pojo.EmpMapper.selectEmp", 1);
            System.out.println(emp);
        }
    }

    /**
     * 基于接口绑定的方式
     *  1.新建数据访问层的接口:  POJOMapper
     *  2.添加mapper中对应的操作的方法
     *      1.方法名要和mapper中对应的操作的节点的id要一致
     *      2.返回类型要和mapper中对应的操作的节点的resultType要一致
     *      3.mapper中对应的操作的节点的参数必须要在方法的参数中声明
     *  3.Mapper.xml 中的namespace必须要和接口的完整限定名要一致
     *  4.修改mybatis全局配置文件中的mappers,采用接口绑定的方式:
     *        <mapper class="cn.tulingxueyuan.mapper.EmpMapper"></mapper>
     *  5.一定要将mapper.xml和接口放在同一级目录中,只需要在resources新建和接口同样结构的文件夹就行了,生成就会合并在一起
     *
     * @throws IOException
     */
    @Test
    public void test02(){
        try (SqlSession session = sqlSessionFactory.openSession()) {
                       // 通过session创建Mapper接口实现类,用来执行SQL
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            Emp emp = mapper.selectEmp(1);
            System.out.println(emp);
        }
    }

    /**
     * 基于注解的方式
     * 1.在接口方法上面写上对应的注解,如下@Select
     *@Select("select * from emp where id=#{id}")
     * 注意:
     *      注解可以和xml共用, 但是不能同时存在方法对应的xml的id
     *
     */
    @Test
    public void test03(){
        try (SqlSession session = sqlSessionFactory.openSession()) {
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            Emp emp = mapper.selectEmp(1);
            System.out.println(emp);
        }
    }
}

四、增删改查的基本操作

4.1 通过xml实现mapper

EmpDao.java

其实就是mapper接口,叫法不同而已

package cn.tulingxueyuan.dao;
import cn.tulingxueyuan.bean.Emp;

public interface EmpDao {
    public Emp findEmpByEmpno(Integer empno);
    public int updateEmp(Emp emp);
    public int deleteEmp(Integer empno);
    public int insertEmp(Emp emp);
}

 

EmpDao.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">
<!--namespace:编写接口的全类名,就是告诉要实现该配置文件是哪个接口的具体实现-->
<mapper namespace="cn.tulingxueyuan.dao.EmpDao">
    <!--
        select:表示这个操作是一个查询操作
        id表示的是要匹配的方法的名称
        resultType:表示返回值的类型,查询操作必须要包含返回值的类型
        #{属性名}:表示要传递的参数的名称
    -->
    <select id="findEmpByEmpno" resultType="cn.tulingxueyuan.bean.Emp">
        select * from emp where empno = #{empno}
    </select>
    <!--增删改查操作不需要返回值,增删改返回的是影响的行数,mybatis会自动做判断-->
    <insert id="insertEmp">
        insert into emp(empno,ename) values(#{empno},#{ename})
    </insert>
    <update id="updateEmp">
        update emp set ename=#{ename} where empno = #{empno}
    </update>
    <delete id="deleteEmp">
        delete from emp where empno = #{empno}
    </delete>
</mapper>

 

MyTest.java

package cn.tulingxueyuan.test;
import cn.tulingxueyuan.bean.Emp;
import cn.tulingxueyuan.dao.EmpDao;
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 org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyTest {
    SqlSessionFactory sqlSessionFactory = null;

    @Before
    public void init() {
        // 根据全局配置文件创建出SqlSessionFactory
        // SqlSessionFactory:负责创建SqlSession对象的工厂
        // SqlSession:表示跟数据库建议的一次会话
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            // 创建sqlSessionFactory
            sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test01() {
        // 通过sqlSessionFactory获取数据库的会话
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 用来接收查询结果的pojo类
        Emp empByEmpno = null;
        try {
            // 获取要调用的接口类
            EmpDao mapper = sqlSession.getMapper(EmpDao.class);
            // 调用方法开始执行
            empByEmpno = mapper.findEmpByEmpno(7369);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
        System.out.println(empByEmpno);
    }

    @Test
    public void test02() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        // 插入方法默认都是返回int,表示受影响的数据条数
        int zhangsan = mapper.insertEmp(new Emp(1111, "zhangsan"));
        System.out.println(zhangsan);
        // 提交查询
        sqlSession.commit();
        // 关闭数据库链接
        sqlSession.close();
    }

    @Test
    public void test03() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        // 更新方法默认都是返回int,表示受影响的数据条数
        int zhangsan = mapper.updateEmp(new Emp(1111, "lisi"));
        System.out.println(zhangsan);
        // 提交查询
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void test04() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        // 删除方法默认都是返回int,表示受影响的数据条数
        int zhangsan = mapper.deleteEmp(1111);
        System.out.println(zhangsan);
        // 提交查询
        sqlSession.commit();
        sqlSession.close();
    }
}

 

 

4.2 使用注解实现mapper

EmpDaoAnnotation.java

package cn.tulingxueyuan.dao;
import cn.tulingxueyuan.bean.Emp;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

public interface EmpDaoAnnotation {
    @Select("select * from emp where id= #{id}")
    public Emp findEmpByEmpno(Integer empno);
    @Update("update emp set ename=#{ename} where id= #{id}")
    public int updateEmp(Emp emp);
    @Delete("delete from emp where id= #{id}")
    public int deleteEmp(Integer empno);
    @Insert("insert into emp(id,user_name) values(#{id},#{username})")
    public int insertEmp(Emp emp);
}

也可以直接在mapper接口上使用注解,将要查询的sql语句直接写在注解中,这样就可以不编写对应的mapper.xml了

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值