MyBatis基础入门

2022-11-15 13:40:07 浏览数 (1)

1. 持久化与ORM技术

1.1 持久化

在软件开发过程中,我们经常要把程序内存中的数据存放到磁盘(或数据库),或者把磁盘(或数据库)的数据加载到内存。这种把程序数据在“瞬时状态”和“持久状态”间转换的过程我们称之为“持久化”。

1.2 ORM

我们往往使用“关系型数据库”来持久化有一定复杂度的程序数据,在Java中,这种持久化是使用JDBC来实现的。用过JDBC的人都知道,JDBC的代码重复性很高,而且冗余代码很多,特别是从ResultSet到对象的相互转换上。有没有更高效的方式来简化这些持久化操作呢?有的,这就是所谓的ORM(Object Relaction Mapping)技术。

        ORM,就是对象关系映射,我们通过一些配置方式,把对象和关系型数据库表的结构一一对应起来,这样只要编写少量的查询语句(比如SQL),我们就可以让程序制动化的把SQL执行结果填充到对象中,免去了编写大量的JDBC代码。Java最常用的ORM框架就是Hibernate和MyBatis。

1.3 MyBatis简介

1.3.1 Hibernate与MyBatis

Hibernate是Java平台上的ORM老大哥,非常自动化,几乎不用编写任何的SQL语句就可以实现常见的CURD操作甚至是复杂查询。但Hibernate也有它的弊端,就是模型复杂,学习成本高,由于可以不编写SQL,因此性能有可控性有些差,要用好它不是一件容易的事。

MyBatis是Java平台上ORM的后起之秀,模型简单,学习成本非常低,性能可控性很高。但相比Hibernate而言,MyBatis的所有数据库操作都要自己写SQL语句,因此代码量比Hibernate要高一些,但相对于原生的JDBC还是有很高的开发效率。

1.3.2 MyBatis的相关资料

官方文档:mybatis – MyBatis 3 | 简介

        官方源码:https://github.com/mybatis/mybatis-3

2 试用MyBatis框架

        下面以Sqlserver数据库作为示范来讲解MyBatis的使用。

2.1 在项目中导入MyBatis框架jar包

        使用MyBatis开发数据访问层,只需要导入mybatis-3.x.x.jar和对应的JDBC驱动即可。以下使用两种方式导入相关依赖。

(1)直接导入

(2)使用Maven

代码语言:javascript复制
<dependencies>
		<!-- JDBC MySQL 驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.8</version>
		</dependency>
		<!-- MyBatis 核心依赖 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.2.5</version>
		</dependency>	
</dependencies>

2.2 设置MyBatis框架的配置信息

MyBatis框架需要两种配置文件:“主配置文件”和“查询映射配置文件”。

主配置文件(mybatis.xml),用于配置“数据库会话工厂”,主要作包括:1)数据库连接(连接池)信息;2)指定其他查询映射配置文件的位置。

查询映射配置文件(CategoryMapper.xml),一般用于配置某实体类(Category.java)的CURD操作所涉及的SQL语句。

如果是Maven项目,这些配置信息都应该放在Resources目录中。

(1)创建“MyBatis主配置文件”

        在“类目录”下创建名为mybatis.xml(文件名可自定)的文件,配置内容如下:

代码语言:javascript复制
<?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/MyCinema" />
				<property name="username" value="root" />
				<property name="password" value="1234" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="mapper/CategoryMapper.xml" />
	</mappers>
</configuration>

其中,“property”包裹部份用于配置数据库的连接信息(JDBC驱动、数据库url、用户名和密码);而“蓝色字体”部分用于指定针对实体类的查询映射配置文件的位置。

(2)创建查询映射配置文件

        创建数据实体类Category.java和实体映射文件CategoryMapper.xml。

        以下实体类表示电影分类,与数据库的Category表对应。

代码语言:javascript复制
public class Category {
	private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

Category的查询映射文件的结构如下所示:

代码语言:javascript复制
<?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="mycinema.dao.CategoryMapper">
  	<select id="fetchById" parameterType="int" resultType="mycinema.entity.Category">
    	select * from Category where id=#{id}
  	</select>
</mapper>

每添加一个实体配置文件,就应该在主配置文件(mybatis.xml)中加入一个<mapper>元素,以告知框架要把该实体加入到映射中,详见上一节mybatis.xml配置中的蓝色字体部份。

注意点:

1)mapper元素的namespace与select元素的id共同决定了一个SQL语句的“坐标”,在MyBatis框架执行查询时,使用坐标标识所调用的SQL

2)SQL语句中的参数通过“#{参数名}”的方式声明,而参数类型需要通过select元素的parameterType来声明

3)select元素通过resultType来声明返回类型(实体对象)

2.3 创建MyBatis数据库会话(SqlSession)并执行查询

代码语言:javascript复制
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
try {
	Category c = (Category)session.selectOne("mycinema.dao.CategoryMapper.fetchById",1);
	System.out.println(c.getName());
} finally {
	session.close();
}

注意:

        1)SqlSessionFactoryBuilder对象:用于构建SqlSessionFactory,此后这个类就不需要存在了。因此SqlSessionFactoryBuilder实例的最佳范围是方法范围 (也就是本地方法变量)。

        2)SqlSessionFactory对象:一旦被创建,应该在你的应用执行期间都存在。没有理由释放或重新创建它。使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次,因此 SqlSessionFactory 的最佳范围是应用范围,最简单的就是使用单例模式或者静态单例模式。

        3)SqlSession对象:每个线程都应该有它自己的SqlSession实例。SqlSession 的实例不能被共享,它是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态字段甚至是实例字段中。如果你在使用Web框架,则要考虑把SqlSession 放在一个和HTTP请求对象相似的范围内(Open Session In View)。关闭 Session 很重要,你应该确保使 用 finally 块来关闭它。下面的示例就是一个确保 SqlSession 关闭的基本模式:

代码语言:javascript复制
SqlSession session = sqlSessionFactory.openSession();
try {
  		// do work
} finally {
		session.close();
}

对于增删改的操作,需要使用SqlSession的commit方法实现提交:

代码语言:javascript复制
SqlSession session = sqlSessionFactory.openSession();
try {
  		// do work
		session.commit();	//增删改需要提交事务
} finally {
		session.close();
}

4)SqlSession的selectOne方法表示查询返回单一对象;如果返回的是多个对象的集合,应使用selectList方法;这些数据库操作方法的第一个参数都是String,该String值指定了要操作的SQL语句的坐标(即SQL查询映射文件中配置的“mapper元素的namespace select元素的id”)。

5)SqlSession的insert()、delete()和update()方法则分别对应于配置文件的<insert>、<delete>、<update>元素,完整的配置信息参考如下。

代码语言:javascript复制
<?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="mycinema.dao.CategoryMapper">
  	<select id="fetchById" parameterType="int" resultType="mycinema.entity.Category">
    	select * from Category where id=#{id}
  	</select>
  	<select id="getAll" resultType="mycinema.entity.Category">
    	select * from Category
  	</select>
	<insert id="add" parameterType="mycinema.entity.Category">
		insert into Category(name) values(#{name})
	</insert>
	<update id="update" parameterType="mycinema.entity.Category">
		update Category set name=#{name} where id=#{id}
	</update>
	<delete id="delete" parameterType="int">
		delete from Category where id=#{id}
	</delete>
</mapper>

3 使用映射器(Mapper)接口操作SQL语句

      上述的查询中,使用映射语句“mycinema.dao.CategoryMapper.fetchById”指定要执行的SQL,这种弱类型的使用方式容易出问题:缺乏编程提示和编译检查。为此,MyBatis还提供了强类型的使用方式——映射器(Mapper)方式。

        实际上,MyBatis中所谓的“映射器”接口就是我们所说的“数据访问对象”(DAO)接口。

        该使用方式的具体做法如下:

(1)在查询配置文件CategoryMapper.xml中,把mapper元的namespace属性指定为一个映射器接口(CategoryMapper)的完全限定类名。

代码语言:javascript复制
<?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="mycinema.dao.CategoryMapper">
  <select id="fetchById" parameterType="int" resultType="mycinema.entity.Category">
    select * from Category where id = #{id}
  </select>
</mapper>

(2)在映射器接口(mycinema.dao.CategoryMapper)中声明方法(fetchById),该方法的签名与配置文件的select元素匹配(方法名与select元素id相同,返回值类型和参数类型也必须对应)。

代码语言:javascript复制
public interface CategoryMapper {
	public Category fetchById(int id);
}

(3)按上设置后,我们就可以通过CategoryMapper接口实现前面的查询了:

代码语言:javascript复制
SqlSession session = factory.openSession();
try {
	CategoryMapper mapper = session.getMapper(CategoryMapper.class);
	Category c = mapper.fetchById(1);
	System.out.println(c.getName());
} finally {
	session.close();
}

这种调用方式是通过接口动态代理来实现的,并不需要为接口添加实现类。映射器的调用方式是强类型的,符合对象化思想,不容易出错,在实际中更为常用。

4. 为实体类提供别名(简称)

        上述的很多<select>元素的resultType属性都声明了实体类的完全限定名,冗长的类名实在不方便。我们可以在主配置文件的开头加入“类型别名”<typeAliases>声明,通过<typeAlias>来指定完全限定类名的简称,以简化类名的书写。

代码语言:javascript复制
<configuration>
	<typeAliases>
		<typeAlias type="mycinema.entity.Category" alias="Category"/>
		……
	</typeAliases>
	<environments default="development">
		……
	</environments>
<configuration>

此后,在实体配置文件中,就可以使用Category这样的别名来指向实体类了。

        另外,还可以通过<package>元素,直接定义包名,所有没有指定包名的实体类,都以此为默认包。

代码语言:javascript复制
<typeAliases>
		<package name="mycinema.entity"/>
</typeAliases>

5 插入数据后获取自动增长的主键ID

5.1 MySQL数据库中的auto_increatement实现主键自增长

        在许多数据库管理系统中(如MySQL和SQL Server),可以把在建表时把主键定义为自增长的整数,插入数据时不需要提供该主键,由数据库维护其自增长逻辑。

MyBatis查询配置中的<insert>元素可以自动实现该自增长主键获取功能,不需要额外的查询,在插入成功后自动的为实体对象(参数)赋值新的主键值。通过配置<insert>元素的useGeneratedKeys属性和keyProperty即可实现该功能,具体配置如下:

代码语言:javascript复制
<insert id="add" parameterType="Category" useGeneratedKeys="true" keyProperty="id">
		insert into Category(name) values(#{name})
</insert>

5.2 Oracle中通过序列(sequence)实现自增长

      Oracle数据库并没有提供表字段的自增长设置,如果是整型字段,可以通过序列(sequence)来获取自增长值,再插入到数据库中。

        以下示例假设Oracle数据库中有一个名为seq_cinema_cate的序列,演示了如何实现插入自增长并返回自增长ID。

代码语言:javascript复制
<insert id="add" parameterType="Category">
		<selectKey keyProperty="id" order="BEFORE" resultType="int">
			select seq_cinema_cate.nextval from dual
		</selectKey>
		insert into Category(id, name) values(#{id}, #{name})
</insert>

6 使用jdbc.properties配置数据源

在主配置文件中,数据库连接信息可以硬编码在配置中,也可以通过<properties resource="jdbc.properties" />元素,在外部以properties文件方式提供,我们在mybatis中可以用以下方式引用properties中的配置。

代码语言:javascript复制
<properties resource="jdbc.properties"/>
	<typeAliases>…</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>

其中,jdbc.properties配置文件的结构如下所示。

代码语言:javascript复制
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=1234
jdbc.url=jdbc:mysql://localhost:3306/MyCinema

0 人点赞