Mybatis

MyBatis是一个apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了Google code,并改名为MyBatis,2013年11月迁移到GitHub。
MyBatis是一个实现了数据持久化的开源框架,就是对JDBC进行封装。

优点:

  • 与jdbc相比减少代码量
  • 是最简单的持久化框架,小巧且简单易学
  • 灵活,不对现有程序和数据库设计造成影响。SQL写在xml里,从程序代码彻底分离,降低耦合度
  • 提供XML标签,支持编写动态SQL语句
  • 提供映射标签,支持对象与数据库的ORM字段关系映射

缺点:

  • SQL语句编写工作量较大
  • SQL语句依赖于数据库,移植性差,不能随意更换数据库

核心接口和类
SqlSessionFactoryBuilder-build()->SqlSessionFactory-openSession()->SqlSession

开发方式

原生方式
Mapper代理

使用mybatis原生接口

  1. 新建Maven工程,在pom.xml中添加org.mybatis、mysql-connector-java依赖
<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>com.jnf</groupId>
  <artifactId>myBatis1</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>myBatis1</name>
  <build/>
  <dependencies>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis</artifactId>
  		<version>3.4.5</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>8.0.19</version>
  	</dependency>
  </dependencies>
</project>
  1. 在resources下新建config.xml,配置mybatis运行环境
<?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>
	<!-- 配置MyBatis运⾏环境 -->
	<environments default="development">
		<environment id="development">
			<!-- 配置JDBC事务管理 -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- POOLED配置JDBC数据源连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
				<property name="url"
					value="jdbc:mysql://localhost:3306/mybatis?
				useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"></property>
				<property name="username" value="root"></property>
				<property name="password" value="12345"></property>
			</dataSource>
		</environment>
	</environments>
</configuration>
  1. 在数据库中建mybatis表;创建Account实体类(long id;String username;String password;int age)
  2. 新建Mapper.xml,写SQL
    namespace 通常设置为⽂件所在包+⽂件名的形式。
    insert 标签表示执⾏添加操作。
    select 标签表示执⾏查询操作。
    update 标签表示执⾏更新操作。
    delete 标签表示执⾏删除操作。
    id 是实际调⽤ MyBatis ⽅法时需要⽤到的参数。
    parameterType 是调⽤对应⽅法时参数的数据类型。
<mapper namespace="mapper.Mapper">
	<insert id="save" parameterType="entity.Account">
		insert into t_account(username,password,age) values(#{username},#{password},#{age})
	</insert>
</mapper>
  1. 将Mapper.xml注册到config.xml中
<!-- 注册AccountMapper.xml -->
<mappers>
	<mapper resource="mapper/Mapper.xml"></mapper>
</mappers>
  1. 测试
public class Test1 {
	public static void main(String[] args) {
		//加载MyBatis的配置文件
		InputStream inputStream=Test.class.getClassLoader().getResourceAsStream("config.xml");
		//得到SqlSession三步
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(inputStream);
		SqlSession sqlSession=sqlSessionFactory.openSession();
		//SQL语句位置
		String statement="mapper.Mapper.save";
		//创建实体类对象
		Account account=new Account(1L,"张三","123123",22);
		//执行插入和提交
		sqlSession.insert(statement,account);
		sqlSession.commit();
	}
}

使用Mapper代理实现自定义接口

  1. 创建自定义接口,定义相关方法
    AccountRepository.java
public interface AccountRepository {
	public int save(Account account);
	public int update(Account account);
	public int deleteById(long id);
	public List<Account> findAll();
	public Account findById(long id);
}
  1. 创建与方法对应的xml
    AccountRepository.xml
<mapper namespace="repository.AccountRepository">
	<insert id="save" parameterType="entity.Account">
		insert into t_account(username,password,age) 
			values(#{username},#{password},#{age})
	</insert>
	<update id="update" parameterType="entity.Account">
		update t_account set username=#{username},password=#{password},
			age=#{age} where id=#{id}
	</update>
	<delete id="deleteById" parameterType="long">
		delete from t_account where id=#{id}
	</delete>
	<!-- 返回的虽是List集合,但规定resultType写集合中数的类型 -->
	<select id="findAll" resultType="entity.Account"><!-- 查询需要写返回类型 -->
		select * from t_account;
	</select>
	<select id="findById" parameterType="long" resultType="entity.Account"><!-- 查询需要写返回类型 -->
		select * from t_account where id=#{id};
	</select>
</mapper>

其中,mapper的namespace是接口的全类名
每个方法的id是方法名
parameterType是操作的参数类型
resultType是接口中对应方法的返回值类型(如果返回值是数据库中被影响的行数,如增删改,一定是int,此时可省略不写)

  1. 在config.xml中注册AccountRepository.xml
<mappers>
	<!-- 注册AccountMapper.xml -->
	<mapper resource="mapper/Mapper.xml"></mapper>
	<!-- 注册AccountRepository.xml -->
	<mapper resource="repository/AccountRepository.xml"></mapper>
</mappers>
  1. 测试
public class Test2 {
	public static void main(String[] args) {
		InputStream inputStream=Test.class.getClassLoader().getResourceAsStream("config.xml");
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(inputStream);
		SqlSession sqlSession=sqlSessionFactory.openSession();
		//获取实现接口的代理对象
		AccountRepository accountRepository=sqlSession.getMapper(AccountRepository.class);
		//测试插入
		Account account=new Account(6,"李四","12345",20);
		accountRepository.save(account);
		sqlSession.commit();
		sqlSession.close();
	}
}

查找数据举例

一对一

一个学生只能属于一个班级,为一对一

  1. 创建学生、班级表格
use mybatis;
create table student(
	id bigint primary key,
	name varchar(11),
	cid bigint
)
create table clazz(
	id bigint primary key,
	name varchar(11)
)
  1. 创建对应实体类
import lombok.Data;

@Data
public class Student {
	private long id;
	private String name;
	private Clazz clazz;
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", clazz=" + clazz + "]";
	}
}
import java.util.List;
import lombok.Data;

@Data
public class Clazz {
	private long id;
	private String name;
	private List<Student> student;
	public String toString() {
		return "Clazz [id=" + id + ", name=" + name + "]";
	}
	
}
  1. 创建通过id找学生的方法
public interface StudentRepository {
	public Student findById(long id);
}
  1. 创建xml写SQL语句
    StudentRepository.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="repository.StudentRepository">

	<!-- 将查询出的结果集按照下列规范对应到实体类 -->
	<resultMap id="studentMap" type="entity.Student">
		<id column="id" property="id"></id>
		<result column="name" property="name"></result>
		<association property="clazz" javaType="entity.Clazz">
			<id column="cid" property="id"></id>
			<result column="cname" property="name"></result>
		</association>
	</resultMap>
	
	<!-- findById方法使用上面定义的studentMap规范 -->
	<select id="findById" parameterType="long" resultMap="studentMap">
		select s.id,s.name,c.id as cid,c.name as cname from student s,clazz c 
		where s.id = #{id} and s.cid = c.id
	</select>
</mapper>
  1. 在config.xml中注册
<!-- 注册StudentMapper.xml -->
<mapper resource="repository/StudentRepository.xml"></mapper>
  1. 测试
public class Test3 {

	public static void main(String[] args) {
		InputStream inputStream=Test.class.getClassLoader().getResourceAsStream("config.xml");
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(inputStream);
		SqlSession sqlSession=sqlSessionFactory.openSession();
		//获取实现接口的代理对象
		StudentRepository studentRepository=sqlSession.getMapper(StudentRepository.class);
		System.out.println(studentRepository.findById(1));
		sqlSession.commit();
		sqlSession.close();
	}
}

一对多

一个班级可以有多个学生,为一对多

相较一对一的改变是,在写SQL语句的结果映射中,用collection替代association,且其中有ofType属性表示集合内元素的类型:

 <resultMap id="classesMap" type="entity.Clazz">
 <id column="cid" property="id"></id>
 <result column="cname" property="name"></result>
 <collection property="students" ofType="entity.Student">
 <id column="id" property="id"/>
 <result column="name" property="name"/>
 </collection>
 </resultMap>

查询语句当然也变了:

<select id="findById" parameterType="long" resultMap="classesMap">
 select s.id,s.name,c.id as cid,c.name as cname from student s,clazz c 
 where c.id = #{id} and s.cid = c.id
</select>

多对多

一个顾客可以购买多种商品,一个商品也可以由多位顾客购买,是多对多关系

多对多的实质为一对多,传入顾客的id寻找顾客购买的所有货物(或传入货物的id寻找购买过的所有顾客),等同于传入班级id返回所有该班级的学生,因此其实就是一对多

逆向工程

MyBatis 框架需要:实体类、⾃定义 Mapper 接⼝、Mapper.xml
传统的开发中上述的三个组件需要开发者⼿动创建,逆向⼯程可以帮助开发者来⾃动创建三个组件,减
轻开发者的⼯作量,提⾼⼯作效率

使用:

  1. 新建Maven工程,导入依赖
<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>com.jnf</groupId>
  <artifactId>myBatis1</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>myBatis1</name>
  <build/>
  <dependencies>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis</artifactId>
  		<version>3.4.5</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>8.0.19</version>
  	</dependency>
  	<dependency>
  		<groupId>org.projectlombok</groupId>
  		<artifactId>lombok</artifactId>
  		<version>1.16.18</version>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis.generator</groupId>
  		<artifactId>mybatis-generator-core</artifactId>
  		<version>1.3.5</version>
  	</dependency>
  </dependencies>
</project>
  1. 创建generatorConfig.xml
    1、jdbcConnection 配置数据库连接信息。
    2、javaModelGenerator 配置 JavaBean 的⽣成策略。
    3、sqlMapGenerator 配置 SQL 映射⽂件⽣成策略。
    4、javaClientGenerator 配置 Mapper 接⼝的⽣成策略。
    5、table 配置⽬标数据表(tableName:表名,domainObjectName:JavaBean 类名)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
 PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
 "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
	<context id="testTables" targetRuntime="MyBatis3">
		<!-- 配置数据库连接信息,相当于config.xml -->
		<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;
							characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"
			userId="root" password="12345">
		</jdbcConnection>

		<!-- 配置JavaBean的生成策略,相当于实体类 -->
		<javaModelGenerator targetPackage="entity"
			targetProject="./src/main/java">
		</javaModelGenerator>

		<!-- 配置SQL映射文件生成策略,相当于repository.xml -->
		<sqlMapGenerator targetPackage="repository"
			targetProject="./src/main/java">
		</sqlMapGenerator>

		<!-- 配置Mapper接口的生成策略 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="repository" targetProject="./src/main/java">
		</javaClientGenerator>

		<!-- 配置数据表生成策略 -->
		<table tableName="t_user" domainObjectName="User"></table>
	</context>
</generatorConfiguration>
  1. 创建执行类
public class GeneratorTest {
	public static void main(String[] args) {
		List<String> warings = new ArrayList<String>();
		boolean overwrite = true;
		String genCig = "/generatorConfig.xml";
		File configFile = new File(GeneratorTest.class.getResource(genCig).getFile());
		ConfigurationParser configurationParser = new ConfigurationParser(warings);
		Configuration configuration = null;
		try {
			configuration = configurationParser.parseConfiguration(configFile);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XMLParserException e) {
			e.printStackTrace();
		}
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = null;
		try {
			myBatisGenerator = new MyBatisGenerator(configuration, callback, warings);
		} catch (InvalidConfigurationException e) {
			e.printStackTrace();
		}
		try {
			myBatisGenerator.generate(null);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

执行后,将自动生成实体类、Mapper接口、Mapper.xml

延迟加载

上面举的例子,使用学生的id到两张表中查询数据,无论最后是否需要班级的信息,都要查询两张表。
延迟加载可以在特定的情况下访问特定的数据库,在其他情况下可以不访问某些表,在一定程度上减少了java应用与数据库的交互次数。

使用:

  1. 在config.xml中开启延时加载
<settings>
	<!-- 打印SQL -->
	<setting name="logImpl" value="STDOUT_LOGGING" />
	<!-- 开启延迟加载 -->
	<setting name="lazyLoadingEnabled" value="true" />
</settings>
  1. 在接口中添加方法
public Student findByIdLazy(long id);
public Classes findByIdLazy(long id);
  1. Repository.xml中,添加查询语句和结果集
<resultMap id="studentMapLazy" type="entity.Student">
	<id column="id" property="id"></id>
	<result column="name" property="name"></result>
	<association property="clazz" javaType="entity.Clazz"
		select="repository.ClassesRepository.findByIdLazy" column="cid">
	</association>
</resultMap>
<select id="findByIdLazy" parameterType="long" resultMap="studentMapLazy">
	select * from student where id = #{id}
</select>
<select id="findByIdLazy" parameterType="long" resultType="com.southwind.entity.Classes">
	select * from classes where id = #{id}
</select>

缓存

使用缓存可以减少java应用与数据库的交互次数。
比如查询一个对象,第一次查询后将查询到的对象保存到缓存中,当下一次查询时直接取出即可。但如果该对象被改变,需要同时也清空缓存,避免查询到之前的信息。

一级缓存:SqlSession级别,默认开启,且不能关闭。
同一个SqlSession执行两次相同的SQL语句时,第二次查询直接从缓存中获取;而不同的SqlSession之间缓存的数据区域互不影响。
二级缓存:Mapper级别,默认关闭,可以开启。
二级缓存是跨SqlSession的,作用域是Mapper的同一个namespace,不同的Sql两次执行相同namespace下的SQL语句,第二次直接从二级缓存中获取数据。

二级缓存可以使用MyBatis自带的二级缓存,也可以用ehcache(一个缓存框架)

自带

  1. config.xml配置开启二级缓存
<settings>
	<!-- 开启二级缓存 -->
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. Mapper.xml配置
<cache></cache>
  1. 实体类实现序列化接口
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account implements Serializable {
	private long id;
	private String username;
	private String password;
	private int age;
}

ehcache

  1. pom.xml添加依赖
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-ehcache</artifactId>
	<version>1.0.0</version>
</dependency>
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache-core</artifactId>
	<version>2.4.3</version>
</dependency>
  1. 添加ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<diskStore/>
	<defaultCache
	maxElementsInMemory="1000"
	maxElementsOnDisk="10000000"
	eternal="false"
	overflowToDisk="false"
	timeToIdleSeconds="120"
	timeToLiveSeconds="120"
	diskExpiryThreadIntervalSeconds="120"
	memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>
  1. config.xml配置开启二级缓存
<settings>
	<!-- 开启二级缓存 -->
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. Mapper.xml中配置
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
	<!-- 缓存创建之后,最后⼀次访问缓存的时间⾄缓存失效的时间间隔 -->
	<property name="timeToIdleSeconds" value="3600"/>
	<!-- 缓存⾃创建时间起⾄失效的时间间隔 -->
	<property name="timeToLiveSeconds" value="3600"/>
	<!-- 缓存回收策略,LRU表示移除近期使⽤最少的对象 -->
	<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>

实体类不需要实现序列化接口。

MyBatis动态SQL

假设有表:

idnameage
006张三22
008李四20

使用三个条件查询:
select * from student where id=#{id} and name= #{name} and age=#{age}
能正确查出来。如果只使用两个条件:
select * from student where id=#{id} and name= #{name}
照理说能够查出,但实际情况是这句话会变成
select * from student where id=#{id} and name= #{name} and age=null
而得到空结果。
或者在数据插入时,如果只有部分属性发生改变,重写所有会带来资源浪费。此时都需要使用动态SQL。

where+if:一个 if 标签里写一个条件,考虑到如果第一个标签不在SQL语句会出错,用 where 就能判断关键字是否需要加上

<select id="findByAccount" parameterType="entity.Account"
	resultType="entity.Account">
	select * from t_account
	<where>
		<if test="id!=0">
			id = #{id}
		</if>
		<if test="username!=null">
			and username = #{username}
		</if>
		<if test="password!=null">
			and password = #{password}
		</if>
		<if test="age!=0">
			and age = #{age}
		</if>
	</where>
</select>

where+choose+when

<select id="findByAccount" parameterType="entity.Account"
	resultType="entity.Account">
	select * from t_account
	<where>
		<choose>
			<when test="id!=0">
				id = #{id}
			</when>
			<when test="username!=null">
				username = #{username}
			</when>
			<when test="password!=null">
				password = #{password}
			</when>
			<when test="age!=0">
				age = #{age}
			</when>
		</choose>
	</where>
</select>

trim
trim 标签中的 prefix 和 suffix 属性会被⽤于⽣成实际的 SQL 语句,会和标签内部的语句进⾏拼接,如果语句前后出现了 prefixOverrides 或者suffixOverrides 属性中指定的值,MyBatis 框架会⾃动将其删除。

<select id="findByAccount" parameterType="entity.Account"
	resultType="entity.Account">
	select * from t_account
	<trim prefix="where" prefixOverrides="and">
		<if test="id!=0">
			id = #{id}
		</if>
		<if test="username!=null">
			and username = #{username}
		</if>
		<if test="password!=null">
			and password = #{password}
		</if>
		<if test="age!=0">
			and age = #{age}
		</if>
	</trim>
</select>

set+if
用于update语句

<update id="update" parameterType="entity.Account">
	update t_account
	<set>
		<if test="username!=null">
			username = #{username},
		</if>
		<if test="password!=null">
			password = #{password},
		</if>
		<if test="age!=0">
			age = #{age}
		</if>
	</set>
	where id = #{id}
</update>

foreach
可以迭代生成一系列值,主要用于SQL的in语句

<select id="findByIds" parameterType="entity.Account"
	resultType="entity.Account">
	select * from t_account
	<where>
		<foreach collection="ids" open="id in (" close=")" item="id" separator=",">
			#{id}
		</foreach>
	</where>
</select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值