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

目录

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

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对象,用于执行SQLStatement stmt = conn.createStatement();// ResultSet用于接收执行SQL的结果    stmt用于执行SQLResultSet 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;@Beforepublic void init() {Configuration configuration = new Configuration();configuration.configure("chapter1/hibernate.cfg.xml");sessionFactory = configuration.buildSessionFactory();}@Afterpublic void destroy() {sessionFactory.close();}@Testpublic 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();}}}@Testpublic 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();}}}@Testpublic 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 {@Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic 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 {@AutowiredJpaStudentDao jpaStudentDao;@Beforepublic void init() {JpaStudent jpaStudent = new JpaStudent("张三", 12, "121");jpaStudentDao.save(jpaStudent);}@Testpublic 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  DefaultSqlSessionFactorySqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);// 通过SqlSessionFactory获取数据源执行器  DefaultSqlSessionSqlSession session = sqlMapper.openSession();try {// 执行查询 底层执行jdbc//User user = (User)session.selectOne("com.tuling.mapper.selectById", 1);// 通过mapper对象执行sqlUserMapper 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;}@Overridepublic 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 configurationPUBLIC "-//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 mapperPUBLIC "-//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 EMPSET username=#{username}WHERE id=#{id}</update><delete id="deleteEmp">DELETE FROM empWHERE 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;@Beforepublic void before(){// 从 XML 中构建 SqlSessionFactoryString 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*/@Testpublic void test01() {// 通过sqlSessionFactory获取sqlSession去执行sqltry (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*/@Testpublic void test02(){try (SqlSession session = sqlSessionFactory.openSession()) {// 通过session创建Mapper接口实现类,用来执行SQLEmpMapper 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**/@Testpublic 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 mapperPUBLIC "-//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;@Beforepublic void init() {// 根据全局配置文件创建出SqlSessionFactory// SqlSessionFactory:负责创建SqlSession对象的工厂// SqlSession:表示跟数据库建议的一次会话String resource = "mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);// 创建sqlSessionFactorysqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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);}@Testpublic 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();}@Testpublic 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();}@Testpublic 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了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux的中间件

我们先补充点关于awk的内容 awk的用法其实很广。 $0 表示整条记录 变量&#xff1a; NF 一行中有多少个字段&#xff08;表示字段数&#xff09; NR &#xff1a; 代表当前记录的序号&#xff0c;从1开始计数。每读取一条记录&#xff0c;NR的值就会自动增加1。&#xff08;…

鸿蒙TypeScript入门学习第5天:【TypeScript 运算符】

1、TypeScript 运算符 运算符用于执行程序代码运算&#xff0c;会针对一个以上操作数项目来进行运算。 考虑以下计算&#xff1a; 7 5 12复制以上实例中 7、5 和 12 是操作数。 运算符 用于加值。 运算符 用于赋值。 TypeScript 主要包含以下几种运算&#xff1a; 算…

NEO 学习之 MLE(最大似然估计)

文章目录 简单题目MLE 在不同的分布的运用正态分布指数分布均匀分布泊松分布 如何理解 最大似然估计&#xff1f; 就是我们先取出一堆样本&#xff0c;得到一个L( θ \theta θ) 函数&#xff0c;然后的话&#xff0c;这个是关于 θ \theta θ 的一个函数&#xff0c;那么由于存…

C++的入门学习

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一、C关键字(C98) 二、命名空间 2.1 引入 ​编辑2.2 命名空间定义 2.3 命名空间的使用 三. C输入&输出 四.缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 1.全缺省参数 2…

HTTP 协议的基本格式

一 HTTP是什么 超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff0c;HTTP&#xff09;是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。 HTTP协议的主要特点包括&#xff1a; 无连…

七、Audio,Paper or Kindle:What‘s the best way to read a book?听书、纸书、电纸书,阅读方式该怎么选?

Whats the most effective way to read a book?Should you stick to paper books you can flip the pages, dog-ear and write notes in the margin?What about Kindle or other eReaders, which let you download new books instantly and cheaply?Is it okay to listen to…

经验分享:企微文档是什么?对企业有什么用处?

许多在企业单位工作的小伙伴应该都用过企业微信&#xff0c;这是一个专为企业打造的高效办公平台。然而&#xff0c;你是否了解其中的一个功能—企微文档呢&#xff1f;在这篇文章中&#xff0c;我将详细的解读一下企微文档是什么和它对企业的益处。 那么&#xff0c;什么是企微…

六、Django开发

六、Django开发 1.新建项目2.创建app2.1 第一种方法&#xff1a;2.2 利用pycharm中tools工具直接创建app 3.设计表结构&#xff08;django&#xff09;4.在MySQL中生成表5.静态文件管理6.部门管理6.1 部门列表 7.模板的继承8.用户管理8.1初识Form1.views.py2.user_add.html 8.2…

7_springboot_shiro_jwt_多端认证鉴权_自定义AuthenticationToken

1. 目标 ​ 本小节会先对Shiro的核心流程进行一次回顾&#xff0c;并进行梳理。然后会介绍如果应用是以API接口的方式提供给它方进行调用&#xff0c;那么在这种情况下如何使用Shiro框架来完成接口调用的认证和授权。 2. 核心架构 引用官方的架构图&#xff1a; 2.1 Subje…

go: go.mod file not found in current directory or any parent directory.如何解决?

这个错误表明你正在执行 go get 命令&#xff0c;但是当前目录或任何父目录中都找不到 go.mod 文件。这可能是因为你的项目还没有使用 Go Modules 进行管理。 要解决这个问题&#xff0c;有几种方法&#xff1a; go mod init <module-name> 其中 <module-name>…

第四篇:3.3 无效流量(Invalid traffic) - IAB/MRC及《增强现实广告效果测量指南1.0》

翻译计划 第一篇概述—IAB与MRC及《增强现实广告效果测量指南》之目录、适用范围及术语第二篇广告效果测量定义和其他矩阵之- 3.1 广告印象&#xff08;AD Impression&#xff09;第三篇广告效果测量定义和其他矩阵之- 3.2 可见性 &#xff08;Viewability&#xff09;第四篇广…

Unix信号处理

信号的基本概念我已经在上一节中简单介绍了&#xff0c;大家可以去看我的上一篇博客&#xff1a; Unix中的进程和线程-2-CSDN博客 1.信号的产生 kill函数&#xff1a; #include <signal.h> #include <fcntl.h> #include<t_stdio.h> //自定义信号处理函数,n为…

云服务器8核32G配置报价大全,腾讯云、阿里云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

鸿蒙手机cordova-plugin-camera不能拍照和图片不显示问题

鸿蒙手机cordova-plugin-camera不能拍照和图片不显示问题 一、运行环境 1、硬件 手机型号&#xff1a;NOVA 7 系统&#xff1a;HarmonyOS版本 4.0.0 2、软件 android SDK platforms&#xff1a;14.0(API Level 34)、13.0&#xff08;API Level 33&#xff09; SDK Build-T…

CentOS系统下Docker的安装教程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

K8S之Secret的介绍和使用

Secret Secret的介绍Secret的使用通过环境变量引入Secret通过volume挂载Secret Secret的介绍 Secret是一种保护敏感数据的资源对象。例如&#xff1a;密码、token、秘钥等&#xff0c;而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使…

【Linux】TCP网络套接字编程+守护进程

文章目录 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09;单进程版本的服务器客户端通信多进程版本和多线程版本守护进程化的多线程服务器 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09; 为了让我们的代码更规范化&…

车载电子电器架构 —— 诊断数据库开发

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

Python 后端 Flask 使用 Flask-SocketIO、前端 Vue3 实现长连接 Websocket 通信详细教程(更新中)

Flask 安装 Flask-Socketio Flask-SocketIO 第三方库使 Flask 应用程序可以实现客户端和服务器之间的低延迟双向通信。客户端应用程序可以使用 Javascript、Python、C、Java 和 Swift 中的任何 SocketIO 客户端库或任何其他兼容客户端来建立与服务器的永久连接。 Flask-Socke…

基于微信小程序的自习室预约系统的设计与实现

基于微信小程序的自习室预约系统的设计与实现 文章目录 基于微信小程序的自习室预约系统的设计与实现1、前言介绍2、功能设计3、功能实现4、开发技术简介5、系统物理架构6、系统流程图7、库表设计8、关键代码9、源码获取10、 &#x1f389;写在最后 1、前言介绍 伴随着信息技术…