我的Java Web之路55 - ORM框架(MyBatis)初步使用
ccwgpt 2024-10-16 08:01 60 浏览 0 评论
本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人。
目录
- 介绍
- 什么是ORM
- MyBatis概述
- 提供Configuration元数据
- 打开MyBatis的日志开关
- 构造SqlSessionFactory对象
- 生命周期的思维
- 装配SqlSessionFactory对象
- Maven的 src/main/resources 目录
- 获取SqlSession对象
- 提供Mapper元数据
- 创建Mapper接口
- 执行Mapper
- 总结
介绍
从这篇文章开始,我们使用了JDBC来访问数据库,然而却带来一系列问题:
- 有很多代码重复的地方;
- 访问数据库的代码没有独立出来(事实上,应该从Service层独立出来形成DAO层);
- 访问数据库的一些资源没有释放,比如连接、Statement、结果集;
- 每次访问都要建立数据库连接,性能低下;
- 与数据库设计耦合严重;
- 正文代码中仍然有用于测试的添加模拟数据的代码;
- 数据库访问的异常处理不够;
- 等等
其中,代码重复、资源释放、异常处理这三个问题通过这篇文章中引入Spring JDBC框架中的JdbcTemplate帮助我们解决了;
每次访问数据库都需要建立和销毁连接导致的性能低下这个问题通过这篇文章引入连接池帮助我们解决了;
本篇文章通过使用ORM框架来解决与数据库设计耦合严重的问题。
什么是ORM
我们先再查看一下HouseService的代码(可以参考这篇文章),重点关注执行SQL语句的部分,我这里列举一些放在一起:
jdbcTemplate.update("insert into house values(?, ?, ?)", "1", "金科嘉苑3-2-1201", "详细信息"); List<House> list = jdbcTemplate.query("select id,name,detail from house", new HouseMapper()); House house = jdbcTemplate.queryForObject("select id,name,detail from house where id = ?", new Object[]{houseId}, new HouseMapper()); jdbcTemplate.update("update house set id=?, name=?, detail=? where id=?", house.getId(), house.getName(), house.getDetail(), house.getId());
HouseMapper做的事是:
public House mapRow(ResultSet rs, int rowNum) throws SQLException { return new House(rs.getString("id"), rs.getString("name"), rs.getString("detail")); }
我们可以发现访问数据库的本质是:
- 把Java对象(如House)中包含的数据转换 / 映射(存储)到关系数据库的关系表;
- 把关系数据库的关系表中的数据转换 / 映射(读取)到Java对象(如House);
这个就是ORM(Object/Relation Mapping,简称ORM)的含义,即ORM框架就是帮你做这种对象-关系之间的映射的框架。
同时,我们也可以发现上述代码把SQL语句或SQL语句中的某些列名硬编码到代码中,一方面SQL语句如果比较复杂的话会导致代码很乱,可读性降低;另一方面SQL语句的修改会导致代码的修改,因此要重新编译,这就是耦合严重的问题。
还有,这里也有一些比较隐蔽的代码重复,比如每次执行SQL语句都要编写: jdbcTemplate.query 或 jdbcTemplate.queryForObject 或 jdbcTemplate.update;每次将查询结果映射到对象都要编写: public House mapRow(ResultSet rs, int rowNum) throws SQLException 等等。如果能够只用Java面向对象编程的方式将数据持久化那该多好。
比如,我想往数据库的房源表中新增一个房源信息,就直接调用 saveHouse(house) 或者 addHouse(house) 或其他名字的方法即可,而这个方法自动就将house对象的数据映射到某个SQL语句中,并调用 jdbcTemplate.update 来执行该SQL语句。这样我们的代码会更加的清晰、可读、简练。当然,我们必须提供一些元数据:
- 哪个类的哪个方法是执行哪个SQL语句的;
- (参数或结果)对象的各属性与关系表的各列如何一一对应的;
- 等等。
ORM框架有很多,Hibernate、JPA、MyBatis等等。
MyBatis概述
MyBatis的官网是 https://mybatis.org/mybatis-3 :
据说MyBatis在国内用的比较多,国外主要用的是Hibernate,不知道是不是这样,不必深究。
官网对其的介绍是:MyBatis支持定制SQL、存储过程和高级映射,能够去除几乎所有JDBC代码、参数设置、查询结果提取,使用XML和注解提供配置元数据(这点类似于Spring框架)。
截止到撰写本文之时,MyBatis的最新版本是 3.5.3 。你可以手动到它指定的地方下载,实际上是托管在GitHub上(https://github.com/mybatis/mybatis-3/releases)。但是现在我们使用Maven来管理项目的依赖,所以只要在我们的项目POM文件中添加如下依赖即可:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency>
MyBatis需要两种配置元数据,一种用于配置框架的运行参数,比如控制MyBatis的行为,数据源和事务管理器(关于事务,以后再讨论)的参数,用于提供对象-关系映射的元数据在何处等等。这叫做 Configuration 元数据,我们以后就把这个叫做配置元数据也可以的。
另一种配置元数据就是对象-关系映射的元数据,这叫做 Mapper 元数据。
MyBatis的核心有两个接口:
- SqlSessionFactory
- SqlSession
提供Configuration元数据
首先,我们要为MyBatis提供运行参数,即Configuration元数据,我采用基于XML的方式。
那我们要把这个XML文件放到工程结构的哪个位置呢?我们知道,之前Servlet技术中的Web部署描述符web.xml和Spring IoC配置元数据dispatcher.xml是放在 WebContent/WEB-INF 目录下的,既然都是XML文件,那我们就暂时先放在此处吧,同时把这个XML文件命名为 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="UNPOOLED"> <property name="driver" value="org.h2.Driver"/> <property name="url" value="jdbc:h2:~/h2db/houserenter"/> <property name="username" value="sa"/> <property name="password" value=""/> </dataSource> </environment> </environments> <mappers> </mappers> </configuration>
前面的部分是XML声明和根元素声明,大家可以参考这篇文章。
根元素<configuration> 里面最重要的两个元素就是<environments> 和 <mappers>了。
<environments>是用来为各个环境(比如开发环境、测试环境、生产环境等)提供参数配置的,所以子元素就是其单数形式<environment> ,这里可以配置多个<environment>,每个<environment>可以用 id 属性来标识,<environments>使用 default 属性来指定当前使用哪一个<environment>。
而<environment> 中最重要的两个元素是<transactionManager> 和 <dataSource>,它们分别是配置事务管理器和数据源的。事务管理器我们暂时不介绍,数据源的配置有点类似于我们之前使用Druid连接池时的配置(参考这篇文章),不再赘述。
不过,<dataSource>元素有个 type 属性,它有三个MyBatis内置的值:
- UNPOOLED:显然是不使用连接池的方式;
- POOLED:显然是使用连接池的方式,应该是MyBatis以连接池的技术实现了DataSource接口;
- JNDI:使用Java命名和目录接口(Java Naming and Directory Interface)的方式,以后介绍JNDI。
我们后面可以选择不使用连接池的方式(UNPOOLED)、使用MyBatis内置连接池的方式(POOLED)配置数据源。
<mappers> 元素就是用来提供 Mapper 元数据的位置的,因为 Mapper 元数据可以是基于XML或Java注解的,因此该元素有以下四种方式:
<mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper class="org.mybatis.builder.AuthorMapper"/> <package name="org.mybatis.builder"/> </mappers> <!-- 代码来源于MyBatis官网 -->
前面两种是基于XML的,不过第一个是基于工程目录相对位置的,一种是基于文件系统绝对位置的;
后面两种是基于Java注解的,一个是指定某个接口,一个是指定包。
打开MyBatis的日志开关
可以往配置元数据中添加如下元素:
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
这样既可在我们的控制台窗口中输出MyBatis执行SQL语句的日志,这对我们调试程序很有帮助。
构造SqlSessionFactory对象
我们先将原来dispatcher.xml中配置DataSource和JdbcTemplate的部分注释或删除:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="houserenter"/> <!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:h2:~/h2db/houserenter" /> <property name="username" value="sa" /> <property name="password" value="" /> <property name="maxActive" value="20" /> <property name="initialSize" value="1" /> <property name="maxWait" value="60000" /> <property name="minIdle" value="1" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> --> </beans>
既然有了MyBatis的配置元数据,那么MyBatis必然会提供某个组件以便在运行时来读取这些配置元数据,并在后面的运行期间使用这些参数。
这个组件就是 SqlSessionFactoryBuilder 。单从类名上看,可以猜测这个类使用了设计模式中的Builder(建造者)模式,这个以后再介绍。简单理解就好像是造房子一样,先打地基,再浇筑建筑框架,再砌墙,再安装水电,再内部装修这样一步一步构造出一个房子出来。具体到MyBatis这个组件就是类似于先造各个environment元素对象,再造各个mapper元素对象,再造其他的配置元素对象这样一步一步构造一个SqlSessionFactory对象出来。
实际上,SqlSessionFactoryBuilder这个组件提供了很多接口给我们调用:
大家可以自行查看其源代码或者JavaDoc。我采用的是:
public SqlSessionFactory build(InputStream inputStream)
现在还有一个问题就是,我们到底要构造多少个SqlSessionFactory对象?是造一个然后给所有其他组件用呢,还是每个组件都造一个,还是每个请求都造一个?这就是SqlSessionFactory对象的生命周期的问题,即一个SqlSessionFactory对象到底存活多长时间(本质是在内存多长时间)?
根据官网的介绍,最佳实践就是我们只需要在应用开始运行时构造一个SqlSessionFactory对象即可,该对象在应用的整个运行期间都可以一直供其他组件对象使用,在应用结束运行时销毁即可,官网也不建议重复生成多个SqlSessionFactory对象。
既然如此,这就意味着我们需要编写某种代码来确保我们每次使用的都是同一个SqlSessionFactory对象,而不会生成多个SqlSessionFactory对象,我们可以使用设计模式中的单例模式来达到这个目的。
但是,Spring IoC就可以帮助我们实现这一点,所以我们不必再重复制造轮子了。我们可以使用Spring IoC的基于Java配置生产和装配Bean的方式(可以参考这篇文章)来实现:
package houserenter.config; 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; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); return new SqlSessionFactoryBuilder().build(inputStream); } }
Spring IoC默认是使用单例模式来生成Bean,因此,上述代码在应用运行期间只会执行一次,即只生成一个SqlSessionFactory对象。同时,我提供了MyBatis配置元数据的位置,暂时先只写该XML文件的名字,看是否能够找到 WebContent/WEB-INF/ 目录下的 mybatis-config.xml 。
上述代码我放在了 houserenter.config包中:
事实上,我们也可以使用Spring IoC基于XML的方式来构造SqlSessionFactory对象,不过这需要使用到另外一个项目依赖,即MyBatis与Spring集成的项目,后面再介绍。
生命周期的思维
这里插一个题外章节。
事实上,世间一切都有生命周期,不管是有生命的,没有生命的,客观的,主观的,都是如此。
我们的编程世界中也是如此,我们可以经常使用这个思维来设计我们的程序,特别是在面向对象编程中。
一个对象一般都包括生成、初始化、使用中、销毁前、销毁等阶段。当然,根据领域的不同,可以划分成的生命周期阶段也不同。
而我们使用过的Spring IoC,其实就可以管理Bean的生命周期,这个以后再介绍。
装配SqlSessionFactory对象
现在,我们可以在需要访问数据库的组件中注入生成的SqlSessionFactory对象,比如在我们的租房网应用中的HouseService组件中:
@Autowired private SqlSessionFactory sqlSessionFactory;
事实上,现在我们就可以来验证一下,重新发布租房网应用,在Eclipse中重新启动Tomcat,然而天不尽如人意,出现了以下异常:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'houseRenterController': Unsatisfied dependency expressed through field 'houseService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'houseService': Unsatisfied dependency expressed through field 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in houserenter.config.MybatisConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is java.io.IOException: Could not find resource mybatis-config.xml
当然,此异常的根本原因是找不到配置元数据 mybatis-config.xml,显然有两种解决方案:
- 要不移动 mybatis-config.xml
- 要不代码中使用绝对路径
我想肯定能够移动到某个位置下,让代码仅仅使用文件名就能够找到该文件的。经过不断的试错,原来放到 src/main/java 目录底下即可。
通过官网我们得知,只要是放到 classpath 下MaBatis就可以找到基于XML的配置元数据,而我们现在是使用Maven来管理项目,其默认源码路径就是 src/main/java ,既然Java类都在此目录下,那么此目录显然是属于 classpath 的。
Maven的 src/main/resources 目录
大家可以执行 Maven build 命令(可以参考这篇文章),然后看看打包好的war包是一个什么样的结构。在Windows系统中的Eclipse中可以快速的导航到文件系统中的工程目录下:
进一步进入 target 目录即可看到打包好的 house-renter-0.0.1-SNAPSHOT.war ,使用解压缩工具 WinRAR 打开该文件:
可以看到,Maven管理的源码目录 src/main/java 在打好的war包里面对应的是 WEB-INF/classes 目录。
事实上,我们也可以在Eclipse中该工程的属性对话框中看到这种对应关系:
为了便于管理这种除了Java源码之外的资源(Maven把这种配置文件归为资源),设置了另外一个默认的目录,这就是 src/main/resources (可以到官方文档 https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html 查看标准的Maven工程结构)
我们也可以建立这样一个目录,然后执行 Maven -> Update Project... 这个工程右键菜单命令即可(可以参考这篇文章)。现在我们的租房网应用的工程结构如下:
我们可以再次发布应用、启动Tomcat验证一下,异常消除了,可以正常启动了。
获取SqlSession对象
有了SqlSessionFactory对象,我们就可以不断的从其中获取SqlSession对象,正如它的类名所示那样,它就像是一个工厂,一个制造SqlSession对象的工厂,这就是设计模式中的工厂模式。
我们暂时先别管工厂模式是什么样的,要想获取SqlSession对象,只需要调用它的 openSession() 方法即可:
SqlSession session = sqlSessionFactory.openSession();
不过,SqlSession对象是一种资源,使用完它之后需要释放,可以使用 try-catch-finally 语法:
SqlSession sqlSession = sqlSessionFactory.openSession(); try { //这里使用sqlSession访问数据库 } finally { sqlSession.close(); }
不过,Java为我们提供了一种更加简练的方式,就是带资源声明的 try-catch 语法,此语法好像是Java SE 8 版本才引入的:
try (SqlSession sqlSession = sqlSessionFactory.openSession()) { //这里使用sqlSession访问数据库 }
实际上,从上面的代码中我们也可以看出,SqlSession对象只适合局部使用,即它的生命周期是方法级别的,意思就是调用某个方法时才生成该对象,该方法执行完后就销毁该对象。
MyBatis官网也描述它为不是线程安全的,最好是每个线程都有自己的一个SqlSession对象,而不是多个线程共用一个SqlSession对象。
提供Mapper元数据
SqlSession对象可以简单理解为一个到数据库的连接,类似于JDBC的Connection对象。
要想执行SQL,最起码还需要SQL语句吧。当然,MyBatis将SQL语句、参数绑定、对象-关系映射等数据都以 Mapper 元数据的形式来提供。
Mapper 元数据可以使用XML,也可以使用Java注解,当然,XML的方式应该是功能最全面的。
下面就使用XML的方式来为我们的租房网应用提供 Mapper 元数据,当然,目前主要是针对房源表的操作。
同样,我们将在 Maven 的 src/main/resources 目录中建立 HouseMapper.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 namespace="houserenter.mapper.HouseMapper"> <update id="dropTableIfExistsHouse"> drop table if exists house </update> <update id="cteateTable"> create table house(id varchar(20) primary key, name varchar(100), detail varchar(500)) </update> <insert id="insert" parameterType="houserenter.entity.House"> insert into house(id, name, detail) values(#{id}, #{name}, #{detail}) </insert> <select id="selectAll" resultType="houserenter.entity.House"> select id,name,detail from house </select> <select id="selectById" parameterType="java.lang.String" resultType="houserenter.entity.House"> select id,name,detail from house where id = #{id} </select> <update id="updateById" parameterType="houserenter.entity.House"> update house set id=#{id}, name=#{name}, detail=#{detail} where id=#{id} </update> </mapper>
前面的部分依旧是XML声明和根元素声明,大家可以参考这篇文章。不过,现在的根元素变成了 <mapper> 。它有一个很重要的属性 namespace,最好设置为相应的Mapper接口的全限定名称。
这里主要用到了以下三个子元素:
- <update>
- <insert>
- <select>
它们要执行的SQL语句显然与其元素名相呼应。重要的是它们都有一个 id 属性,这个属性很重要,必须与相应的Mapper接口中的方法名相同。
然后是给每个SQL语句传参数的方式,采用属性 parameterType ,其值可以是任何Java类,最好写全限定的类名,即带包路径的类名。而SQL语句中使用 #{fieldName} 的形式来与Java类的属性进行自动绑定,所以必须确保Java类中有fieldName这个属性。
最后是查询SQL语句的结果集映射到Java类对象中,采用属性 resultType ,其用法与属性 parameterType 类似。不过需要注意的是,如果结果集返回的是多条记录,那么将映射到一个Java集合类上,此时属性 resultType 的值应该设置为该集合类所包含的元素的类型,而不是集合类本身的类型。
事实上,还有一个属性 resultMap 可以结果集到Java类对象的映射,暂不讨论。
现在可以看到,Mapper元数据的主要作用就是:
- 映射SQL语句与Mapper接口的方法,通过元素 id 和接口方法名;
- 映射参数与Java类对象,通过属性 parameterType;
- 映射结果集与Java类对象,通过属性 resultType 或属性 resultMap (不能同时用);
最后,我们需要将此 Mapper 元数据的位置告诉 MyBatis ,即需要修改 mybatis-config.xml 中的 <mappers> 元素:
<mappers> <mapper resource="HouseMapper.xml"/> </mappers>
创建Mapper接口
接下来,我们需要创建Mapper接口,这样我们的其他组件才能够使用Mapper接口来访问数据库,我们可以新建一个包,叫做 mapper,然后在该包下新建一个接口 HouseMapper :
其内容如下:
package houserenter.mapper; import java.util.List; import houserenter.entity.House; public interface HouseMapper { int dropTableIfExistsHouse(); int cteateTable(); int insert(House house); List<House> selectAll(); House selectById(String id); int updateById(House house); }
可以看到:
- 这个接口的全限定名称 houserenter.mapper.HouseMapper 正是Mapper元数据中声明的 namespace 。
- 这个接口的方法也与Mapper元数据中的SQL语句是一一对应的,并且方法名与相应元素的 id 是相同的。
- 参数的类型也与 parameterType 的值相同。
- 一般执行写操作SQL语句的方法,返回的是 int 类型的值,表示受影响的记录有多少条;执行查询SQL语句的方法有两种,一种是只返回唯一的一条记录,此时方法的返回类型就是我们的领域类型(比如,House);一种是返回多条记录,此时方法的返回类型一般是一个集合类,但往往使用泛型(以后再讨论)指定了集合所包含元素的类型(比如,List<House>)。
- 实际上,执行查询SQL语句的方法的返回类型可以不是领域类型,而是Java中的 Map<> 类型。
执行Mapper
现在,可以使用我们的Mapper接口来操作数据库了。
首先,我们要从 SqlSession 对象中获取 Mapper 接口的对象,比如,我们的 HouseMapper:
HouseMapper houseMapper = sqlSession.getMapper(HouseMapper.class);
然后,就可以直接调用 Mapper 接口的方法了,比如:
houseMapper.dropTableIfExistsHouse(); houseMapper.cteateTable(); houseMapper.insert(new House("1", "金科嘉苑3-2-1201", "详细信息")); houseMapper.insert(new House("2", "万科橙9-1-501", "详细信息"));
最后,千万别忘记了执行写操作SQL语句之后,要手动提交事务,否则执行写操作SQL语句将会回滚,即导致执行无效:
sqlSession.commit();
因为,我们在 mybatis-config.xml 配置的事务管理器是 JDBC :
<transactionManager type="JDBC"/>
这样,结合前面装配 SqlSessionFactory 对象,获取 SqlSession 对象,我们的 HouseService 的内容如下:
package houserenter.service; import java.io.IOException; import java.util.List; import javax.annotation.PostConstruct; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import houserenter.entity.House; import houserenter.mapper.HouseMapper; @Service public class HouseService { @Autowired private SqlSessionFactory sqlSessionFactory; @PostConstruct public void generateMockHouses() throws IOException { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { HouseMapper houseMapper = sqlSession.getMapper(HouseMapper.class); houseMapper.dropTableIfExistsHouse(); houseMapper.cteateTable(); houseMapper.insert(new House("1", "金科嘉苑3-2-1201", "详细信息")); houseMapper.insert(new House("2", "万科橙9-1-501", "详细信息")); sqlSession.commit(); } } public List<House> findHousesInterested(String userName) { // 这里查找该用户感兴趣的房源,省略,改为用模拟数据 try (SqlSession sqlSession = sqlSessionFactory.openSession()) { HouseMapper houseMapper = sqlSession.getMapper(HouseMapper.class); return houseMapper.selectAll(); } } public House findHouseById(String houseId) { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { HouseMapper houseMapper = sqlSession.getMapper(HouseMapper.class); return houseMapper.selectById(houseId); } } public void updateHouseById(House house) { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { HouseMapper houseMapper = sqlSession.getMapper(HouseMapper.class); houseMapper.updateById(house); sqlSession.commit(); } } }
总结
最后,大家可以将应用重新发布并启动Tomcat进行验证,应该没什么大问题,一切正常。
可以看到,我们除了
- 增加MyBatis的配置元数据 mybatis-config.xml
- 使用Spring IoC基于Java的生成SqlSessionFactory这个Bean,MybatisConfig.java
- 增加MyBatis的Mapper元数据 HouseMapper.xml
- 创建Mapper接口 HouseMapper.java
这些新增文件以外,剩下的只需要修改我们的HouseService组件即可,这就是分层、MVC的好处。
不过,整体上看来,HouseService组件的代码比之前使用Spring JDBC时(参考这篇文章)差不多,此时虽然把SQL语句、参数绑定、结果映射从Java代码中剔除出去了,但是,获取SqlSession对象、获取Mapper接口对象等又出现了类似之前提到的代码重复。这个问题可以进一步使用另外一个项目依赖来解决,后续再介绍。
- ORM的核心功能(本质)就是其名称所示的对象-关系映射;
- 主要是三方面的映射:SQL语句与Mapper接口方法、SQL语句的参数与方法参数、SQL语句的执行结果集与方法返回类型;
- MyBatis的核心组件是SqlSessionFactory、SqlSession;
- SqlSessionFactory的生命周期是应用级别,即单例;
- SqlSession是非线程安全的,生命周期最好是线程级别、或请求级别、或方法级别;
- MyBatis的使用主要步骤是:提供配置元数据、Mapper元数据、Mapper接口、构造单例的SqlSessionFactory对象、方法内用带资源的try-catch语法获取SqlSession对象、获取Mapper接口对象、调用Mapper接口的方法、必要时手动提交事务;
- 生命周期的思维很重要,大家要经常使用,不管是在编程中,还是在管理工作中,甚至是生活中;
- 本篇文章涉及的设计模式有单例、建造者(Builder)、工厂(Factory),以后专门介绍它们;
相关推荐
- 团队管理“布阵术”:3招让你的团队战斗力爆表!
-
为何古代军队能够以一当十?为何现代企业有的团队高效似“特种部队”,有的却松散若“游击队”?**答案正隐匿于“布阵术”之中!**今时今日,让我们从古代兵法里萃取3个核心要义,助您塑造一支战斗力爆棚的...
- 知情人士回应字节大模型团队架构调整
-
【知情人士回应字节大模型团队架构调整】财联社2月21日电,针对原谷歌DeepMind副总裁吴永辉加入字节跳动后引发的团队调整问题,知情人士回应称:吴永辉博士主要负责AI基础研究探索工作,偏基础研究;A...
- 豆包大模型团队开源RLHF框架,训练吞吐量最高提升20倍
-
强化学习(RL)对大模型复杂推理能力提升有关键作用,但其复杂的计算流程对训练和部署也带来了巨大挑战。近日,字节跳动豆包大模型团队与香港大学联合提出HybridFlow。这是一个灵活高效的RL/RL...
- 创业团队如何设计股权架构及分配(创业团队如何设计股权架构及分配方案)
-
创业团队的股权架构设计,决定了公司在随后发展中呈现出的股权布局。如果最初的股权架构就存在先天不足,公司就很难顺利、稳定地成长起来。因此,创业之初,对股权设计应慎之又慎,避免留下巨大隐患和风险。两个人如...
- 消息称吴永辉入职后引发字节大模型团队架构大调整
-
2月21日,有消息称前谷歌大佬吴永辉加入字节跳动,并担任大模型团队Seed基础研究负责人后,引发了字节跳动大模型团队架构大调整。多名原本向朱文佳汇报的算法和技术负责人开始转向吴永辉汇报。简单来说,就是...
- 31页组织效能提升模型,经营管理团队搭建框架与权责定位
-
分享职场干货,提升能力!为职场精英打造个人知识体系,升职加薪!31页组织效能提升模型如何拿到分享的源文件:请您关注本头条号,然后私信本头条号“文米”2个字,按照操作流程,专人负责发送源文件给您。...
- 异形柱结构(异形柱结构技术规程)
-
下列关于混凝土异形柱结构设计的说法,其中何项正确?(A)混凝土异形柱框架结构可用于所有非抗震和抗震设防地区的一般居住建筑。(B)抗震设防烈度为6度时,对标准设防类(丙类)采用异形柱结构的建筑可不进行地...
- 职场干货:金字塔原理(金字塔原理实战篇)
-
金字塔原理的适用范围:金字塔原理适用于所有需要构建清晰逻辑框架的文章。第一篇:表达的逻辑。如何利用金字塔原理构建基本的金字塔结构受众(包括读者、听众、观众或学员)最容易理解的顺序:先了解主要的、抽象的...
- 底部剪力法(底部剪力法的基本原理)
-
某四层钢筋混凝土框架结构,计算简图如图1所示。抗震设防类别为丙类,抗震设防烈度为8度(0.2g),Ⅱ类场地,设计地震分组为第一组,第一自振周期T1=0.55s。一至四层的楼层侧向刚度依次为:K1=1...
- 结构等效重力荷载代表值(等效重力荷载系数)
-
某五层钢筋混凝土框架结构办公楼,房屋高度25.45m。抗震设防烈度8度,设防类别丙类,设计基本地震加速度0.2g,设计地震分组第二组,场地类别为Ⅱ类,混凝土强度等级C30。该结构平面和竖向均规则。假定...
- 体系结构已成昭告后世善莫大焉(体系构架是什么意思)
-
实践先行也理论已初步完成框架结构留余后人后世子孙俗话说前人栽树后人乘凉在夏商周大明大清民国共和前人栽树下吾之辈已完成结构体系又俗话说青出于蓝而胜于蓝各个时期任务不同吾辈探索框架结构体系经历有限肯定发展...
- 框架柱抗震构造要求(框架柱抗震设计)
-
某现浇钢筋混凝土框架-剪力墙结构高层办公楼,抗震设防烈度为8度(0.2g),场地类别为Ⅱ类,抗震等级:框架二级,剪力墙一级,混凝土强度等级:框架柱及剪力墙C50,框架梁及楼板C35,纵向钢筋及箍筋均采...
- 梁的刚度、挠度控制(钢梁挠度过大会引起什么原因)
-
某办公楼为现浇钢筋混凝土框架结构,r0=1.0,混凝土强度等级C35,纵向钢筋采用HRB400,箍筋采用HPB300。其二层(中间楼层)的局部平面图和次梁L-1的计算简图如图1~3(Z)所示,其中,K...
- 死要面子!有钱做大玻璃窗,却没有钱做“柱和梁”,不怕房塌吗?
-
活久见,有钱做2层落地大玻璃窗,却没有钱做“柱子和圈梁”,这样的农村自建房,安全吗?最近刷到个魔幻施工现场,如下图,这栋5开间的农村自建房,居然做了2个全景落地窗仔细观察,这2个落地窗还是飘窗,为了追...
- 不是承重墙,物业也不让拆?话说装修就一定要拆墙才行么
-
最近发现好多朋友装修时总想拆墙“爆改”空间,别以为只要避开承重墙就能随便砸!我家楼上邻居去年装修,拆了阳台矮墙想扩客厅,结果物业直接上门叫停。后来才知道,这种配重墙拆了会让阳台承重失衡,整栋楼都可能变...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- bootstrap框架 (43)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- express框架 (43)
- scrapy框架 (52)
- beego框架 (42)
- java框架spring (43)
- grpc框架 (55)
- 前端框架bootstrap (42)
- orm框架有哪些 (43)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)