【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.0modelVersion>
  <groupId>com.jnfgroupId>
  <artifactId>myBatis1artifactId>
  <version>0.0.1-SNAPSHOTversion>
  <name>myBatis1name>
  <build/>
  <dependencies>
  	<dependency>
  		<groupId>org.mybatisgroupId>
  		<artifactId>mybatisartifactId>
  		<version>3.4.5version>
  	dependency>
  	<dependency>
  		<groupId>mysqlgroupId>
  		<artifactId>mysql-connector-javaartifactId>
  		<version>8.0.19version>
  	dependency>
  dependencies>
project>
  1. 在resources下新建config.xml,配置mybatis运行环境


<configuration>
	
	<environments default="development">
		<environment id="development">
			
			<transactionManager type="JDBC">transactionManager>
			
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver">property>
				<property name="url"
					value="jdbc:mysql://localhost:3306/mybatis?
				useUnicode=true&characterEncoding=UTF-8&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中

<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>
	
	<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>
	
	<mapper resource="mapper/Mapper.xml">mapper>
	
	<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



<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>
	
	
	<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中注册

<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.0modelVersion>
  <groupId>com.jnfgroupId>
  <artifactId>myBatis1artifactId>
  <version>0.0.1-SNAPSHOTversion>
  <name>myBatis1name>
  <build/>
  <dependencies>
  	<dependency>
  		<groupId>org.mybatisgroupId>
  		<artifactId>mybatisartifactId>
  		<version>3.4.5version>
  	dependency>
  	<dependency>
  		<groupId>mysqlgroupId>
  		<artifactId>mysql-connector-javaartifactId>
  		<version>8.0.19version>
  	dependency>
  	<dependency>
  		<groupId>org.projectlombokgroupId>
  		<artifactId>lombokartifactId>
  		<version>1.16.18version>
  	dependency>
  	<dependency>
  		<groupId>org.mybatis.generatorgroupId>
  		<artifactId>mybatis-generator-coreartifactId>
  		<version>1.3.5version>
  	dependency>
  dependencies>
project>
  1. 创建generatorConfig.xml
    1、jdbcConnection 配置数据库连接信息。
    2、javaModelGenerator 配置 JavaBean 的⽣成策略。
    3、sqlMapGenerator 配置 SQL 映射⽂件⽣成策略。
    4、javaClientGenerator 配置 Mapper 接⼝的⽣成策略。
    5、table 配置⽬标数据表(tableName:表名,domainObjectName:JavaBean 类名)。



<generatorConfiguration>
	<context id="testTables" targetRuntime="MyBatis3">
		
		<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&
							characterEncoding=utf-8&serverTimezone=Asia/Shanghai"
			userId="root" password="12345">
		jdbcConnection>

		
		<javaModelGenerator targetPackage="entity"
			targetProject="./src/main/java">
		javaModelGenerator>

		
		<sqlMapGenerator targetPackage="repository"
			targetProject="./src/main/java">
		sqlMapGenerator>

		
		<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>
	
	<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.mybatisgroupId>
	<artifactId>mybatis-ehcacheartifactId>
	<version>1.0.0version>
dependency>
<dependency>
	<groupId>net.sf.ehcachegroupId>
	<artifactId>ehcache-coreartifactId>
	<version>2.4.3version>
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"/>
	
	<property name="memoryStoreEvictionPolicy" value="LRU"/>
cache>

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

MyBatis动态SQL

假设有表:

id name age
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>

你可能感兴趣的:(框架)