持久层 学习笔记

1、JDBC

1-1、简介

  用于java程序连接并访问数据库的一款工具,也是说明书,规定统一了格式,
  进行真正的增删改查操作需要由数据库驱动包(jar)包来实现(jar包由不同厂商提供,其中包含了编译后的Class文件,其中又有很多工具类,以实现GRUD)。同时也产生了不同jar包,对应也有不同的API,造成学习成本高的问题,后由sun公司提供了一套接口,要求所有厂商都需实现此接口,实现了API的统一,提供了规范,这个规范就是jdbc
  jdbc的jar包已经被java包含,但其中大部分都是接口规范,需要实现具体功能需要导入数据库对应的驱动包

实现流程图

在这里插入图片描述

2-1、入门案例

1、准备好要查询的数据后导包,创建lib目录(Folder),导入mysql驱动包(下载),创建引用(选择右键–> Build Path --> Add To Build Path)
在这里插入图片描述
2、导包完成后写JDBC代码

	public static void main(String[] args) throws Exception {
		//1、注册数据库驱动   将创建mysql驱动包反射对象,会动态将其放入DriverManager进行管理
		Class.forName("com.mysql.cj.jdbc.Driver");
		//2、获取数据库连接   通过DriverManager类提供方法获取连接,设置端口、数据库和字符集,时区
		Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai"
				,"root","root");
		System.out.println("连接成功!");
		//3、获取传输器,//Statment为接口,规定了格式,如统一方法的名称
		Statement stat = connection.createStatement();
		//4、发送sql到数据库执行,并返回执行结果  mysql Driver实现的具体方法,
		ResultSet rs = stat.executeQuery("select * from account");
		//5.处理结果(将查询的结果一行行输出到控制台)
		while(rs.next()) {
			int id = rs.getInt("id");
			String name = rs.getString("name");
			double money = rs.getDouble("money");
			System.out.println(id+ "," + name+ "," + money);
		}
		
		/*
		增删改操作:
		//5-1、添加操作
		int rows = stat.executeUpdate("insert into account value(null,'hellen',3500)");
		//5-2、删除操作
		int rows = stat.executeUpdate("delete from account where name = 'hellen' ");
		//5-3、修改操作
		int rows = stat.executeUpdate("update account set money =5000 where name = 'hellen' ");
		System.out.println("影响行数为:"+rows);
		conn.close();
		stat.close();
		*/
		
		//6.释放资源
		rs.close();
		stat.close();
		connection.close();
	}

补充:数据库事务

  • 概念:多个sql语句绑定一起执行,考面试

1、事务四大特性:
原子性:事务作为最小单元不可分割,一条sql语句报错整个sql便都不能执行
一致性:事务执行前后的业务数据之和保持不变
隔离性:事务并发时,事务之间互相隔离
持久性:事务提交后,数据库持久保存在磁盘中
2、事务操作:
默认每一个sql就是一个单独的事务,自动开启提交
事务包含多条sql需手动开启结束
begin开启事务后,需要commit提交事务才会真正修改数据,换成rollback则回滚

思考总结

通过入门案例不难发现JDBC有以下缺点:.
1、大量重复代码,注册连接传输器释放资源..
2、查询语句处理结果集麻烦,需要一个个列获取(参考入门案例)
3、将SQL语句、连接参数写死在程序中,后期上线维护困难 .
除此之外,JDBC虽然作为传统底层方法,访问速度比第三方框架速度快,但却没有连接池(类似常量池,创建一次后直接从池中拿,无需自身创建连接),导致每次都需自身创建关闭连接,耗时效率低下 .
由此针对JDBC源生代码的缺点,引出了一个优秀的持久层(程序连接数据库的部分)框架——Mybatis

2、Mybatis(封装简化JDBC)

2-1、xml配置开发

  • 通过配置两个xml配置文件分别设置连接信息,管理方法等(mybatis.config.xml) 和 书写SQL语句(EmpMapper.xml)

2-1-1、入门案例(查询 xml)

  • 实现查看数据库表。IDE:Eclipse:Simple maven Project ,maven工程目录如下:
    在这里插入图片描述


    各文件连接关系:EmpMapper.xml<—MybatisDemo01.java—>mybatis-config.xml—>EmpMapper.xml—>pojo.Emp.java    A–>B代表A中引用了B


    1、MybatisDemo01
  • 两处配置文件名、Emp类名可修改,
public class MybatisDemo01 {
	public static void main(String[] args) throws Exception {
		//1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂)
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.打开与数据库的连接(即通过工厂对象获取SqlSession对象)
		SqlSession session = factory.openSession();
		//4.通过namespace+id找到并执行SQL语句, 返回处理后的结果
		//EmpMapper.xml, List<Emp>
		List<Emp> list = session.selectList( "EmpMapper.findAll" );
		//5.输出结果
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}
}

2、POJO.Emp

public class Emp {
	//提供私有属性
	private Integer id;
	private String name;
	private String job;
	private Double salary;
	//提供get、set、toString方法..

3-1、EmpMapper.xml

  • namespace:为mapper取名,id:为sql操作取名。
    调用:List list = session.selectList( “EmpMapper.findAll” );
<?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">
<!-- namespace: 用于标识当前这个mapper文件(就是一个名字) 在mybatis程序中需要通过这个名字来定位当前这个mapper文件 
	通过namespace值+id值可以定位要执行的是哪条SQL语句 -->
<mapper namespace="EmpMapper">
	<!-- 通过select,insert,update,delete标签来存放要执行的SQL -->
	<!-- 练习01: 查询emp表中的所有员工信息 -->
	<!-- id属性:要求当前这个文件中的id值必须是独一无二的(不能重复) resultType属性: 指定查询的结果要存放在哪个类型的对象中 -->
	<select id="findAll" resultType="com.java.pojo.Emp">
		select * from emp
	</select>
</mapper>

3-2、mybatis-config.xml

  • default:选中指定id的环境。id:为每个环境做唯一标识。
    type:配置事务管理和连接池。value:数据库基本信息。resource:导入类目录下的指定文件
<?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">
    
<!-- MyBatis的全局配置文件 -->
<configuration>
	<!-- 1.配置开发环境 -->
	<environments default="dev">
		<environment id="dev">
			<!-- 1.1.配置事务管理方式
				JDBC: 将事务交给JDBC管理(mybatis会自动开启事务,但需手动提交)
				MANAGED: 自己手动管理事务 -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 1.2.配置连接池信息, type的取值:
				JNDI: 已过时
				UNPOOLED: 不使用连接池
				POOLED: 使用连接池(可以减少连接创建次数,提高执行效率) -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql:///yonghedb?characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	<!-- 2.导入XxxMapper.xml文件(如果mapper文件有多个,
		可以通过多个mapper标签导入)
		resource属性会直接到类目录(classes)下去找指定位置的文件
	 -->
	<mappers>
		<mapper resource="EmpMapper.xml"/>
	</mappers>
</configuration>

4、pom.xml

  • 四个驱动包,加上驱动包的依赖为八个jar包,没有检查maven本地仓库
<dependencies>
	    <!-- junit单元测试 -->
	    <dependency>
	        <groupId>junit</groupId>
	        <artifactId>junit</artifactId>
	        <version>4.9</version>
	    </dependency>
	    <!-- mysql驱动 -->
	    <dependency>
	        <groupId>mysql</groupId>
	        <artifactId>mysql-connector-java</artifactId>
	        <version>8.0.11</version>
	    </dependency>
	    <!-- mybatis -->
	    <dependency>
	        <groupId>org.mybatis</groupId>
	        <artifactId>mybatis</artifactId>
	        <version>3.2.8</version>
	    </dependency>
	    <!-- 整合log4j -->
	    <dependency>
	        <groupId>org.slf4j</groupId>
	        <artifactId>slf4j-log4j12</artifactId>
	        <version>1.6.4</version>
	    </dependency>
	</dependencies>

2-1-2、入门案例二(增加、删除、修改)

  • 修改mybastis01.java代码,提取共用SqlSession对象,使用单元测试@Test,选中方法名运行才可指定执行
  • 用@Before保证SqlSession对象已经获取。
  • mybatis修改的事务不会自动提交,需要openSession( true )设置自动提交或每次执行完commit手动提交
  • 修改Mapper文件增填执行的sql语句
public class MybatisDemo01 {
	//提取共需对象做成员变量,供给不同方法使用
	SqlSession session = null;
	
	/* @Before标记的方法会在 每个@Test标记的方法之前执行! */
	@Before
	public void beginMethod() throws Exception {
		//1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂)
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.打开与数据库的连接(即通过工厂对象获取SqlSession对象)
		session = factory.openSession( true );//开启自动提交事务
		//4.通过namespace+id找到并执行SQL语句, 返回处理后的结果
	}
	
	//插入 员工信息
	@Test
	public void insert() {
		int rows = session.insert("EmpMapper.insert");
		System.out.println("影响行数:"+ rows);
		session.commit();
	}
	
	//修改 员工信息
	@Test
	public void update() {
		int rows = session.update("EmpMapper.update");
		System.out.println("影响行数:"+ rows);
		session.commit();
	}
	
	//删除 员工信息
	@Test
	public void delete() {
		int rows = session.delete("EmpMapper.delete");
		System.out.println("影响行数:"+ rows);
		session.commit();
	}
}
  • EmpMapper.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">
<!-- namespace: 用于标识当前这个mapper文件,在mybatis程序中通过此名字来定位当前mapper文件 
	通过namespace值+id值可以定位要执行的是哪条SQL语句 -->
<mapper namespace="EmpMapper">
	<!-- 插入 -->
	<select id="insert">
		insert into emp value(null,"马云","教师",800)
	</select>
	<!-- 修改 -->
	<select id="update">
		update emp set salary = 1800 where name = '马云'
	</select>
	<!-- 删除 -->
	<select id="delete">
		delete from emp where name = '马云'
	</select>
</mapper>

2-2、常见错误

  • 1、错误信息: Cause:java.lang.NoSuchMethodException: cn.tedu.pojo.Emp.()
  • 原因:pojo实体类初始化失败,说明缺少创建对象的构造方法。Mybatis底层会基于反射创建Emp类的对象实例,
    创建使用的无参构造。
  • 解决:查看是否时添加了含参构造,导致默认无参构造消失
  • 2、错误信息: Mapped Statements collection does not contain value for EmpMapper.finaAll
  • 2原因:存放sql语句和其对应标识的map集合(Statements collection)中不存在对应的key值。
    Statements collection存储形式:EmpMapper.findAll(key) : select * from emp(value)
  • 解决:Mapper.xml配置文件中的 namespace或id写错了
  • 3、错误信息: Mapped Statements collection already contains value for EmpMapper.update
  • 原因:Statements collection中已经存在相同的key值,Statements collection以map形式存储: namespace+id(key):sql(value)
  • 解决:检查mapper文件中标签的id值是否重复。
  • 4、查询结果封装不到实体类对象中。(数据库数据如何封装到对象中?)
  • 原因:封住查询结果实体类中,会先寻找实体类是否提供提供get、set方法(注意方法名要与属性对应),
    没有提供则通过通过暴力反射将数据库表中列名赋值给同名的属性。
  • 解决:一般将实体类类型设为包装类,防止出现基本类型默认值。同时提供get、set方法用于封装结果集,保持属性名和数据表列名一致。

2-3、log4j日志框架

  • 专门为Java语言提供的一个日志框架, 可以通过log4j打印程序中的日志信息,
  • mybatis默认支持log4j框架。 使用只需两步走:
    1)导入log4j的jar包
    2)导入log4j的配置文件(log4j.properties)即可, 配置文件内容如下:
# Global logging configuration
log4j.rootLogger=DEBUG, Console, LOGFILE
# Console output...
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n

log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.file=./mylog.log
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n
配置文件各参数解析:
DEBUG:日志级别,低于设置级别的信息不输出。(比如设置成error则不输出DEBUG,error级别改于DEBUG)。
Console, LOGFILE:设置输出到控制台和外部文件
ConsoleAppender:日志输出器,往指定的地方输出信息,ConsoleAppender指定往控制台,FileAppender指定往文件输出
PatternLayout、.ConversionPattern:两者配合使用,Pattem设置输出格式,%5p: 日志界别(限制五个字符), %t:线程名称, %m:消息内容, %n换行...更多参数配置参考:[log4j日志pattern配置](https://www.cnblogs.com/sagech/p/9278715.html)。【】、空格 、-、%5 只是为了输出美观,设置还是以 %参数 的形式设置。

输出到文件写法与输出到控制台类似,多设置一行输出目录file=./mylog.log即可

2-4、占位符

  • 数据由前端动态传递,通过占位符在sql中的参数值进行占位,等待数据传递赋值
  • mybatis中有两种占位符, 分别是: #{}, ${}, 最常用的为 #{},占位符两者使用需要注意有所区别
  • 传入的数据往往包含多个(id,name,job…),而session提供的增删改查的方法只能传入两个参数(一个已经被 namespace+id 所占据),所以需要将数据进行封装(Map或实体类对象),后通过占位符将其中的数据进行拆分填充,形成sql语句,完成查询功能。

2-4-1、通过Map对象封装

  • Map集合的Key要和占位符名称保持一致
	/*
	EmpMapper.xml:
		<select id="insert2">
			insert into emp value(null, #{name} , #{job} , #{salary})
		</select>
	*/
	@Test
	public void insert2() {
		Map map = new HashMap();
		map.put("name", "马云");
		map.put("job", "教师");
		map.put("salary",1800);
		int rows = session.insert("EmpMapper.insert2",map);
		System.out.println("影响行数:"+ rows);
		session.commit();
	}

mybatis占位符:#{} 翻译成 JDBC的占位符:(?),如果占位符名称与map的key不一致,便会如下图导入插入一个null值。
原因是:占位符名称接收到value值找不到对应Key值进行存放。当只有一个占位符时才不需要名称保持一致。


2-4-2、通过对象封装

  • 对象的get方法要与占位符名称对应( 如:getIName,#{name} )或
    对象实体类中有与占位符同名的属性名(如:private String job; #{job})
	/*
	<!--修改员工信息: 将马云的职位改为"CEO", 薪资改为80000 -->
	<select id="update2">
		update emp set job = #{job} , salary = #{salary} where name = #{name}
		底层转换为JDBC:update emp set job = ? , salary = ? where name = ? 
	</select>
	*/
	@Test
	public void update2() {
		//创建实体类对象将数据封装到对象中进行传递
		Emp emp = new Emp();
		emp.setName("马云");
		emp.setJob("CEO");
		emp.setSalary(80000.0);		
		int rows = session.update("EmpMapper.update2",emp);
		System.out.println("影响行数:"+ rows);
		session.commit();
	}
将属性名job修改为new_job会出现以下错误: There is no getter for property named 'new_job' in 'class com.java.pojo.Emp' 则说明new_job没有get方法,同时pojo实体类中没有这个属性名。

结论:占位符要与对象的get()方法名称保持对应,或对象类中存在同名私有属性

2-4-3、#{}, ${}的区别

  • #{ } :先通过 ?占位,后再按照占位符名称按位填充,所以当有多个占位符时需要将参数进行封装,同时会拼接引号 “”
    例如:insert into emp value(null, #{name} , #{job} , #{salary}) (插入一条员工信息:包括姓名,工种,薪水)
    第一步:insert into emp value( null, ? , ? , ? )
    第二步:通过封装map,或对象传入多个参数匹配 ? 占位符
    第三步:SQL最终变为:insert into emp value(null, 小明 , 老师 , 1800)

  • ${ }:为某一个SQL片段进行占位, 直接拼接在${}所在位置, 无论参数为一个还是多个占${} 都必须对数据进行封装, 无引号“”
    例如:select #{colname} from emp; (查询员工表的指定列)
    第一步,封装对象传入多个列名(“colname” :“id, name, job”)
    第二步:直接拼接在${}所在位置:select id, name, job from emp;

  • 实际应用问题:
    问题一:、使用#{}动态查询指定列时:select #{colname} from emp ,查询结果为null
    问题二:使用${}动态查询指定id时:select * from emp where id = ${} or 1=1 查询结果却为全部员工, 而用#{}才是查询指定员工

问题一:

	/*
	<!--查询emp表中的所有员工信息, 动态显示要查询的列 -->
	<select id="findAll2"  resultType="com.java.pojo.Emp">
		select #{colname} from emp 
	</select>
	*/
	@Test
	public void findAll2() {
		List<Emp> list = session.selectList("EmpMapper.findAll2","id,name,job");
		for (Emp emp : list) {
			System.out.print(emp);
		}
	}

查询结果为null:

原因:使用 #{ } 会自动拼接引号,导致sql语句变为: select ‘id,name,job’ from emp;
变成了查询‘id,name,job’为一个整体、不存在的列
解决: 使用 ${ } 让其不拼接引号,使其变成一个我们所想要的sql,
同时要求使用\${ }中无论有多少个数据,必须传入一个封装好的类型。具体解决代码如下:
	/*
	<!--查询emp表中的所有员工信息, 动态显示要查询的列 -->
	<select id="findAll2"  resultType="com.java.pojo.Emp">
		select ${colname} from emp 
	</select>
	*/
	@Test
	public void findAll2() {
		HashMap map = new HashMap();
		map.put("colname", "id,name,job");
		List<Emp> list = session.selectList("EmpMapper.findAll2",map);
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}
}

问题二:

	/*
	<select id="findAll3" resultType="com.java.pojo.Emp">
		select * from emp where id = ${name}  
	</select>
	*/
	@Test
	public void findAll3() {
		Map map = new HashMap();
		map.put("id", "5 or 1=1");
		List<Emp> list = session.selectList("EmpMapper.findAll3",map);
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}

只想让用户查询一个却让其查询到了全部员工

原因:${ }会将全部直接进行拼接,sql语句的where 1=1 判断条件永远成立,
此类现象称之为sql注入攻击,容易造成安全隐患,


解决:使用#{}进行约束,使sql连接符失效,即将\${ }换成#{ }即可,#{ }会自动拼接第一个数字,结果如下:

2-5、Mybatis的动态SQL

  • SQL语句根据参数值的不同,来生成不同的SQL语句
  • if标签、where标签、foreach标签(批量处理数据)

2-5-1、if标签

  • test属性存放判断条件,满足条件则会进行sql拼接
    注意小于号转义成 &lt;大于号也可用 &gt; 表示

查找薪资为3000~5000范围内的所有员工:

/*Empmapper.xml:
	<select id="testIf" resultType="com.java.pojo.Emp">
		select * from emp 
		where 1=1 
			//test属性存放布尔表达式,为True才拼接到sql语句中
			<if test="minSal != null">
				and salary &gt; #{minSal} 
			</if>
			<if test="maxSal != null">
				and  salary  &lt; #{maxSal}
			</if>
	</select>
*/
	
	@Test
	public void testIf() {
		Map map = new HashMap();
		map.put("minSal", 3000);
		map.put("maxSal", 5000);
		List<Emp> list = session.selectList("EmpMapper.testIf",map);
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}

2-5-2、where标签

  • where标签包裹if标签条件成立, where标签就会生成where关键字, 并会去掉多余连接词(and、or)

查找薪资为3000~5000范围内的所有员工(where标签修改):

/*
	<select id="testWhere" resultType="com.java.pojo.Emp">
	select * from emp 
		<where>
			<if test="minSal != null">
				and salary &gt; #{minSal} 
			</if>
			<if test="maxSal != null">
				and salary  &lt; #{maxSal}
			</if>
		</where>
	</select>
	*/
	@Test
	public void testWhere() {
		Map map = new HashMap();
		map.put("minSal", 3000);
		map.put("maxSal", 5000);
		List<Emp> list = session.selectList("EmpMapper.testWhere",map);
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}

if、where两者结果均为:


2-5-3、foreach标签(难点)

  • 对数组集合进行遍历,生成sql语句。
  • 其包含属性:collection:传值为数组 / 集合 / map集合,collection值分别为 array / List / map中的key
    open:指定所生成SQL片段的起始符号,通常是左圆括号 (
    close:指定所生成SQL片段的结束符号,通常是右圆括号 )
    item:指定占位符中的名称
    separator:指定占位符中间的分隔符, 通常是逗号 ,

案例1:根据员工的id批量删除员工信息 id数组 int ids = {1,3,5,7}; (只需传入一组参数)

	/*
	<delete id="testDeleteByIds">
		delete from emp where id in
		<foreach collection="array" open="(" item="id" separator="," close=")" >
			#{id}
		</foreach>
	</delete>
	*/
	@Test
	public void testDeleteByIds() {
		int[] ids = {1,3,5,7};
		int rows = session.delete("EmpMapper.testDeleteByIds", ids);
		System.out.println("影响行数:"+ rows);
	}

案例2:根据员工的id批量更新员工信息 id数组 int ids = {2,4,6,8} 涨薪1000; (需传入两组参数)
在这里插入图片描述

思考与总结:

先写出正常的SQL语句,找其中需要代替的位置进行分析,根据业务判断是否需要判断还是循环。
如:select * from emp where salary > 3000 and salary <5000
其中3000,5000为动态值,需要用if进行判断,与where标签搭配去除多余连接词
delete from emp where id in (1,3,5,7);
in需要取多个值这个时候就需要foreach标签对(1,3,5,7)进行处理拼接
动态SQL减少了SQL语句的数量,也使得SQL语句变得更加灵活通用,需要熟悉掌握。

2-6、单元测试框架(junit)

  • 可在不提供main函数, 并且不创建对象的情况下, 直接运行一个非静态的方法
  • 被@Test标记的方法必须符合以下要求:
    1)方法必须是公共的(pubilc), 不能是私有的(private)
    2)方法必须是非静态的(no static)
    3)方法必须是无返回值的(no return)
    4)方法必须是无参数的(no param)
  • 原理:通过反射扫描类中所有带有@Test注解的方法,通过反射创建类对象调用公共的方法,判断是否符合规范,有一个不符合规范就会报错。因此方法不会接收返回值,无法给参数(由反射创建)。

2-7、Mapper接口开发

  • 使用接口开发原因:xml方式namespace+id写错没提示,同时因为方法参数有限经常须对数据进行封装
  • 接口开发需满足的规则:
    1、创建接口,接口的全类名 = namespace
    2、SQL标签的id值 = 接口中的方法名
    3、查询SQL时, resultType的类型要和接口方法的返回值类型对应
    接口返回类型:实体对象(Emp),resultType=“cn.tedu.pojo.Emp”
    接口返回类型:集合(List),resultType=(cn.tedu.pojo.Emp)

使用过程:(如果有占位符,则提供方法参数)
在这里插入图片描述

相较纯xml配置文件开发,需要多创建接口方法,并设置于xml配置对应,同时使用也不再是调用默认提供方法,调用的是自身创建的方法,会在编译期就进行校验。

2-7-1、大致原理

  • Mybatis创建了实现接口的实现类,实现类中重写方法,仍是通过sqlsession的源生增删改查( namespace+id ),
    和原先xml配置文件开发方式差不多,用接口和方法对Mapper.xml进行了一个封装,让使用调用获取代理类创建
    的对象直接调用方法即可。测试代码如下:
public class EmpMapperImpl implements EmpMapper{
	//1.提供变量session,将来能调用对应的SQL
    private SqlSession session;
    //2.通过构造方法为属性赋值
    public EmpMapperImpl(SqlSession session){
        this.session = session;
    }
    @Override
    public List<Emp> findAll() {
    	//4.通过反射,获取全类名
        String namespace = this.getClass().getInterfaces()[0].getName();
        //5.通过线程获取当前方法名,也就是id名
        String id = Thread.currentThread().getStackTrace()[1].getMethodName();
        //3.通过session调用对应的查询语句,关键在于namespace+id,和之前xml的方式一样
        List<Emp> list = session.selectList(namespace + "." + id);
        return list;
    }

	@Override
		public void insert() {
			//获取当前这个类的父接口的全类名(=namespace)
			String interName = this.getClass().getInterfaces()[0].getName();
			//获取当前方法的名字(=SQL标签的id值)
			StackTraceElement[] st = Thread.currentThread().getStackTrace();
			String methodName = st[1].getMethodName();
			session.insert( interName+"."+methodName );
		}
}
	/* 测试自己提供的EmpMapper接口的实现类: 新增员工信息 */
	@Test
	public void testInsert2() {
		//获取EmpMapper接口的子类(我们自己提供)的对象实例
		EmpMapper mapper = new EmpMapperImpl(session);
		mapper.insert();
	}
	
	/* 测试自己提供的EmpMapper接口的实现类(EmpMapperImpl) */
	@Test
	public void testFindAll2() {
		//获取EmpMapper接口的子类(我们自己提供)的对象实例
		EmpMapper mapper = new EmpMapperImpl(session);
		List<Emp> list = mapper.findAll();
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}

2-8、注解开发

  • 无需写配置文件,只需在接口上方法上添加注解(@Select,@Insert,@Update,@Delete…),括号里写SQL语句
  • 优势:操作方便,相较接口和xml配置方式,无需写Mapper.xml配置文件。
    劣势:修改麻烦,修改SQL语句需修改Java源程序,对于后面发布来说需重新编译打包上传。
    同时SQL语句格式定死,不能写动态SQL

在这里插入图片描述

3、结语

作为三大框架之一的持久层框架Mybatis,其底层为JDBC,学习JDBC有助于理解Mybatis。创建Mybatis项目使用的是Maven(构建项目的工具)创建的maven项目,这样可以管理(mybatis,mysql,junit,log4j等它们依赖的jar包)的jar包,Mybatis具体使用又主要分为三种:分别为xml,接口,注解三种方式,同时其还有自己动态SQL的语法,但实质上还是SQL语法进行拼接。主要围绕以上的东西进行了粗略的使用与学习,初始写此篇博文目的是为了日后复习能快速构建并使用整套的Mybatis,对整个流程有大体的认识,因为不是面向看众,所以很多知识点并没有写的非常详细,更多是写给自身日后复习,但在格式上进行做到有所侧重,小白学习如果有文章有错误还请指正,日后有新的学习和感悟会再更新这篇博文。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值