Mybatis
Javaweb —emmm……框架就是开发人员**用的东西
其实以现有的技术,已经能够跑通一个网站了;但是呢,Maven这强大的管理 和框架这么便捷的操作,所以还是用框架写吧
Maven使得之前的导包的操作变得十分简单,从这个阶段开始,IDE工具使用IDEA,但是Eclipse也会操纵,没有什么大问题;对于mybatis,其实还有一个类似的技术是hibernate;后面会提一提;技术挺多的,慢慢来---- 但是底层都差不多
之前的文章中提到了MVC的结构,也就是控制层、视图层和业务逻辑层;控制层为servlet;视图层为response或者jsp;业务逻辑层就是service层;控制层是最重要的,负责接收请求,调用service层;并用view层反馈—web架构;
三层架构包括3层: 界面层(User interface layer )、业务逻辑层(Business logic layer 【biz】)、数据访问层(data access layer);
三层架构就是一种垂直结构;和MVC的分块方式不同
三层的处理请求的交互 : 用户—> 界面层—>业务逻辑层—> 数据访问层---->DB数据库
三层对应的框架:
界面层 ---- > servlet ----> springMVC框架
业务逻辑层---->service----->spring框架管理
数据访问层----->dao ------> mybatis框架
可以先来随便看一段代码
public int editModifyStudent(String stuno,String stuname,String stuclass) {
Connection conn = null;
PreparedStatement state = null;
int count = 0;
try {
conn = DBUtil.getConnection();
conn.setAutoCommit(false);
String sql = "UPDATE student SET stuname=?,stuclass=? WHERE stuno = ?";
state = conn.prepareStatement(sql);
state.setString(1, stuname);
state.setString(2, stuclass);
state.setString(3, stuno);
count = state.executeUpdate();
//一定要记得提交
conn.commit();
} catch (SQLException e) {
if(conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}finally {
DBUtil.close(conn, state, null);
}
return count;
}
这段代码还是使用工具类DBUtil将注册驱动和连接和释放资源的操作封装后的结果;可以看到,其实对于这个操纵;用户真正变得地方是
String sql = "UPDATE student SET stuname=?,stuclass=? WHERE stuno = ?";
state = conn.prepareStatement(sql);
state.setString(1, stuname);
state.setString(2, stuclass);
state.setString(3, stuno);
但是为了正常使用JDBC,却不得不写其他的操作;所谓JDBC编程6步,其实真正改变的就是sql语句而已,按照配置文件的方式,数据库的操作在文件中改即可;【代码量很长;但是代码没有任何亮点,所以需要封装起来】
总结来说就是
封装成工具类DBUtil并没有很好解决上面的问题,所以需要更加彻底的封装
所谓的框架就是一个模板,这个模板 规定了一些条款和内容需要遵循【其实就是写好的东西,我们作为用户来使用,用别人的东西当然就不是随心所欲了】,然后非常简单的加入内容即可;框架中定义了一些功能,这些功能是可以使用的,可以在项目中加入自己的功能,之后这些功能就可以利用框架的功能来实现想要实现的强大的功能
framework是整个或部分系统的可复用的设计,表现为一组抽象的构件和构件之间的交互的方法;框架是开发者指定的骨架和模板;框架就是安全的软件 【半成品的软件,加入自己的功能即可,基础的功能不变】
框架的特点: 框架一般不是全能的,一般针对某一领域有效
Mybatis框架使用java编写提供了的持久层框架包括SQL Mapper和Data access Objects(Daos),解决的问题就是减轻JDBC的复杂性,不用重复创建Connection,Statement,不需要编写释放资源的代码,直接使用java对象,表示结果数据;mybatis是一个sql mapper框架,提供数据库的操作能力,其实就是一个增强的JDBC,使用mybatis,开发人员就只用写sql语句即可,专注业务处理
sql mapper : sql映射 : 可以将数据库表的一行数据看作一个java对象【一个表就是一个java entitiy类】
Mybatis可以封装的功能有:
所以programmer只需要提供sql语句----- mybatis处理sql ---- 就可以得到List集合成java对象;
主配置文件放在maven项目的main/resources目录下
,所以一个module只会对应一个数据库的连接,主配置文件发挥的作用就是数据库绑定;和之前写的配置文件db.properties相同,其中有各种的信息; 这个文件主要存放的是数据库的配置信息,和sql映射文件的位置
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
mybatis-3-config.dtd 就是约束文件的名称;是dtd类型的
接下来就是configuration;这个是根标签,是整个文件的主体
在根标签configuration中有两个子标签,一个是environments;另外一个就是mappers
environments: 环境配置,数据库的连接信息,因为s;表明可以配置多个环境;一个environment对应的就是一个数据库的配置;其属性id是用来唯一标识整个environment的,可以自定义 default属性的值必须和某个environment的id相同,告诉使用哪个数据库的连接;访问哪个数据库
mappers: 指定sql映射文件的位置,使用一个mapper标签指定mapper文件的位置,整个路径是从类路径开始写, 也就是maven编译后从target/classes目录下开始; 这里要将资源给放进来需要在pom.xml中配置资源插件
<environments default="myenv"> default必须和某个数据库的配置的id相同,告诉mybatis使用哪个数据库的连接信息
<environment id="myenv">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/cfengbase?servertimezone=GMT%2B8"/>
<property name="username" value="cfeng"/>
<property name="password" value="********"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="cfeng\dao\StudentDao.xml"/>
mappers>
如果有其他的mapper文件,也可以再次指定;因为environment可以有多个,这里有其他库的操作的mapper文件
//最上面就是指定这是xml,并且字符的编码方式
约束文件的作用 : 限制、检查在当前文件中出现的标签,属性必须符合mybatis的要求
DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
这样就指定了约束文件的位置
<mapper namespace="cfeng.dao.StudentDao">
<select id="selectStudents" resultType="cfeng.entity.Student">
SELECT stuho,stuname,stuclass FROM student ORDER BY stuno;
select>
mapper>
select标签中就直接写select的sql语句就可以正常执行
<select id="selectStudents" resultType="cfeng.entity.Student">
SELECT stuho,stuname,stuclass FROM student ORDER BY stuno;
select>
这个标签表示执行select语句;其中
id
: 表示的是要执行的sql语句的唯一标识;mybatis会使用这个id来找到要执行的sql语句; 可以自定义,这个id就是接口中方法的名称
resultType
:标识这条sql语句执行后得到的resultset;遍历这个reult结果集得到的java对象的类型,值为类的全限定名称
;也就是表中每一行记录的表示的含义首先就是下载Mybatis,直接到GitHub上面下载即可;第一个是压缩包–核心文件jar;第二个是项目的源代码;打开就可以发现就是基于Maven的项目;核心的jar包就一个,但是依赖的jar比较多
使用mybatis的关键步骤就是添加mybatis依赖、编写sql映射文件和主配置文件
这里演示一下mybatis的使用;查询student表
这里第一个简单的例子就是操作简单的表,这里就使用cfengbase中的student表
mysql> DESC student;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| stuno | int | NO | PRI | NULL | auto_increment |
| stuname | varchar(255) | YES | | NULL | |
| stuclass | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
mysql> SELECT * FROM student;
+-------+---------+----------+
| stuno | stuname | stuclass |
+-------+---------+----------+
| 1 | 张三 | HC2001 |
| 2 | 李四 | HC2002 |
| 3 | Cfeng | HC2002 |
| 4 | 王五 | HC2001 |
| 6 | Jning | HC2001 |
| 7 | 里斯 | HC2003 |
| 8 | 卡夫卡 | HC2004 |
| 10 | 里仁 | HC2004 |
| 11 | 小欢 | HC2006 |
+-------+---------+----------+
9 rows in set (0.01 sec)
之后就在IDEA中新建一个新的module来操作这张表;创建一个普通的java SE项目即可,使用quickstart模板
为了能够在项目中使用相关的功能和方法,所以必须加入依赖; 加入后maven就会自动下载了
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.9version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.27version>
dependency>
这里如果导入不成功,可以再reImport一下
这里可以在ponm中加入资源插件,因为mapper文件在java目录下,不再resources中;同时加入之后,还要再加上resources的插件,因为不会保留原来的自动机制
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties
**/ *.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<!-- 还要配置上原来编译资源的路径-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*
false
entity是对应的是数据库中的一张表
这里因为键盘的关系快捷键Alt + Insert不方便,可以进行修改,修改方式就是File–> settings---->Keymap—>输入new ,鼠标右键,然后点击add,就可以修改了,这里就改为了Alt + L;applyu就可以了【eclipse是ctr + N】
这里新建类改为了 Alt + L
生成代码generate改为 : ALt + G
这里生成代码构造器,要按住ctr 才能选择多个属性
所以这里就快捷生成一个实体类
package cfeng.entity;
public class Student {
private int stuid;
private String stuname;
private String stuclass;
public Student() {
}
public Student(int stuid, String stuname, String stuclass) {
this.stuid = stuid;
this.stuname = stuname;
this.stuclass = stuclass;
}
public int getStuid() {
return stuid;
}
public void setStuid(int stuid) {
this.stuid = stuid;
}
public String getStuname() {
return stuname;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public String getStuclass() {
return stuclass;
}
public void setStuclass(String stuclass) {
this.stuclass = stuclass;
}
@Override
public String toString() {
return "Student{" +
"stuid=" + stuid +
", stuname='" + stuname + '\'' +
", stuclass='" + stuclass + '\'' +
'}';
}
}
创建持久层的接口,定义操作数据库的方法
首先创建一个操作student的Dao接口
package cfeng.dao;
import cfeng.entity.Student;
import java.util.List;
/**
* 这个接口用于声明操作Student表的方法
*/
public interface StudentDao {
//查询Student表的所有数据
public List<Student> selectStudents();
}
之后创建其实现类
这个文件是sql的映射文件,mybatis的官方文档中定义了使用的方式,一般一个表对应一个sql映射文件,这个文件就是xml格式的文件【携带数据—sql语句】
这个文件一般放在dao下面,也就是和dao的接口所在的目录相同,因为一个到对应的是一张表,而一个mapper映射文件也是对应的是这张表相关操作;文件名和接口名一致
这个文件可以参考mybatis给得帮助文档
DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cfeng.dao.StudentDao">
<select id="selectStudents" resultType="cfeng.entity.Student">
SELECT stuho,stuname,stuclass FROM student ORDER BY stuno;
select>
mapper>
这样就写好了上面的dao接口声明的查询所有的学生的sql语句;写入到mapper文件
一个项目就一个主配置文件,这个文件中提供了数据库的连接信息和sql映射文件的位置信息【其实就是利用反射机制将其剥离出来,易扩展修改】 ----> ResourceBoundle
<environments default="myenv">
<environment id="myenv">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/cfengbase?servertimezone=GMT%2B8"/>
<property name="username" value="cfeng"/>
<property name="password" value="********"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="cfeng\dao\StudentDao.xml"/>
mappers>
通过mybatis访问数据库;经过上面的步骤,已经能够访问数据库了,接下来就编写mybatis类来执行mysql语句;类路径的根就是target/classes
,那么如何使用mybatis类访问数据库呢?
package cfeng;
import cfeng.entity.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class myApp {
public static void main(String[] args) throws IOException {
//读取主配置文件;使用mybatis的Resources的类方法get可以加载文件 ---- 这个stream交给sessionFactory
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
//通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
//指定要执行的sql语句,并执行
String sql = "cfeng.dao.StudentDao.selectStudents";
List<Student> list = session.selectList(sql);
//操作对象
list.forEach(System.out::println);
//list.forEach(stu -> System.our.println(out));
//关闭sql会话对象
session.close();
}
}
这里最后操作集合使用的是forEach;里面是函数式接口,所以使用Lambda表达式或者方法引用都可以mybatis要求实体类的属性和表的col必须一致,像这里stuid和no不一致,赋值就是默认值;不会给stuno
Student{stuid=0, stuname='小欢', stuclass='HC2006'} -----> mybatis要求实体类的属性和表的col必须一致,像这里stuid和no不一致,赋值就是默认值;不会给stuno
Student{stuno=1, stuname='张三', stuclass='HC2001'}
Student{stuno=2, stuname='李四', stuclass='HC2002'}
Student{stuno=3, stuname='Cfeng', stuclass='HC2002'}
Student{stuno=4, stuname='王五', stuclass='HC2001'}
Student{stuno=6, stuname='Jning', stuclass='HC2001'}
Student{stuno=7, stuname='里斯', stuclass='HC2003'}
Student{stuno=8, stuname='卡夫卡', stuclass='HC2004'}
Student{stuno=10, stuname='里仁', stuclass='HC2004'}
Student{stuno=11, stuname='小欢', stuclass='HC2006'}
Process finished with exit code 0
可以看到完美的输出了结果,将每一个对象都是toString
其实这里一个mapper配置文件就是对应Dao中的一个接口;一个接口对应的一个表; 之前没有mybatis就要创建实体类来实现这个方法,但是方法中真正起作用的是sql语句;所以封装就相当于用xml文件实现了接口,其中的方法就是一个简单的sql语句
这样简单就完成了一个查询所有学生的操作;之后再写一个插入学生的操作;像这种的DML操作在JDBC中就会返回一个int型的值标识影响的行数;之前的时候一般就会接收这个值用来判断,这里也是定义这样的一个方法
//插入新的数据 int为影响数据库的行数
public int insertStudent(Student student);
之后相当于是实现这个方法,在mapper文件中进行配置
<insert id="insertStudent">
INSERT INTO student (stuno,stuname,stuclass) VALUES (#{stuno},#{stuname},#{stuclass});
insert>
这里要实现动态sql;使用的是域操作;在maven的pom文件中使用的是类似简化EL的格式${属性名}来获取property标签的值; 这里相当于是获取Stuent类的属性; 这里是#{属性名} —>要求和前面的查询名一致
之后再Myapp类下面使用session来执行这个方法
但是这样子非常麻烦,所以可以直接再test中的java文件夹下编写测试代码测试功能
package cfeng;
import cfeng.entity.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class TestMybatis {
//测试方法,测试功能
@Test
public void testInsert() throws IOException {
//读取主配置文件;使用mybatis的Resources的类方法get可以加载文件 ---- 这个stream交给sessionFactory
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
//通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
//指定要执行的sql语句,并执行;这里要插入一个学生对象
Student stu = new Student(12,"黄某","HC2001");
String sql = "cfeng.dao.StudentDao.insertStudent";
int count = session.insert(sql,stu);
System.out.println("影响的行数为 :" + count);
}
}
这里出现了一个问题;最开始的时候一直Error:(3, 24) java: 程序包org.junit不存在” ---- 这里是因为版本号不匹配的问题;不应该使用默认的4.11;使用之前在maven中测试成功的4.13.2版本即可
影响的行数为 :1
Process finished with exit code 0
这里再看一下数据库: 插入的数据并没有进入数据库
之前写JDBC的时候,当时做DML操作的时候都会关闭事务的自动提交机制;同时开启事务,当时常犯的错误就是最后忘记commit;导致数据执行不成共;Mybatis也是默认不会自动提交事务的,需要进行手动提交事务
使用sqlSession对象的commit方法就提交事务
加入到上面的程序中,再次查询数据库;可以发现数据插入成功
| 12 | 黄某 | HC2001 |
这个时候,可以再执行一次,看是否会报错,以为主键约束
### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '12' for key 'student.PRIMARY'
可以看到预期结果相同,现在是提交了事务的
看到下面的提示
### SQL: INSERT INTO student (stuno,stuame,stuclass) VALUES (?,?,?);
这虽然是报错的,但是发现其实还是之前的preparedStatement的预编译sql语句的格式;mybatis给封装了
上面的操作虽然知道其执行成功了,但是并不知道sql语句的内容等信息,那么要如何知道呢;这里就可以通过开启日志来获取信息;开启日志的方式很简单;
直接在mybatis.xml文件也就是主配置文件中加入日志配置即可,这样就可以输出执行的sql语句和参数; 配置settings标签; Settings: 控制mybatis的全局行为;而这个
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
注意这里的写法,一旦单词写错就会报错,value为STDOUT_LOGGING — 之前少写了一个G;
之后执行就发现可以
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections. ---->就是数据库连接池
Opening JDBC Connection
Created connection 988850650.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3af0a9da] 就是关闭连接---连接池就是之前的减少时间在最开始打开20个通道是相同的,这样就可以节省时间
==> Preparing: INSERT INTO student (stuno,stuname,stuclass) VALUES (?,?,?);
==> Parameters: 13(Integer), 奥利(String), HC2002(String)
这里不要随便rebuild项目,rebuild之后不识别harmcest-core依赖,所以就重新加了一下本地仓库的jar♂测试发现rebuild之后就是相当于初始化,所以这个时候之前添加的依赖的内存就没有了,这个时候在pom.xml位置选择Reimport就可以了
这个类就是读取主配置文件的时候要使用其类方法;其是mybatis中的一个类,负责读取主配置文件
InputStream in = Resources.getResourceASStream(“mybatis.xml”); — 就是编译后的target/classes目录下
这个类唯一的作用就是使用build方法【方法传入的参数就是上面的输入流】来build一座sqlSessionFactiory
这是一个重量级对象;程序创建一个对象耗时比较长,使用资源较多,在一个项目创建一个就可以了;【像之前的connection也是重量级的对象,所以必须使用pool来减少时间】
public interface SqlSessionFactory {
SqlSession openSession();//非自动提交事务的
SqlSession openSession(boolean var1);//这个的参数就是是否开启自动提交机制true就是自动
SqlSession openSession(Connection var1);
SqlSession openSession(TransactionIsolationLevel var1);
SqlSession openSession(ExecutorType var1);
SqlSession openSession(ExecutorType var1, boolean var2);
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
SqlSession openSession(ExecutorType var1, Connection var2);
Configuration getConfiguration();
}
这就是要给接口,其实现类有两个:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
通过作用就是获取SqlSession对象;使用的就是工厂模式
所以如果想要自动提交,就SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession(true);z这个对象和之前的Connection对象相同都有操作数据库的事务方法rollback
SqlSession接口,定义了很多操作数据库的方法
public interface SqlSession extends Closeable {
<T> T selectOne(String var1);
<T> T selectOne(String var1, Object var2);
<E> List<E> selectList(String var1);
<E> List<E> selectList(String var1, Object var2);
<E> List<E> selectList(String var1, Object var2, RowBounds var3);
<K, V> Map<K, V> selectMap(String var1, String var2);
<K, V> Map<K, V> selectMap(String var1, Object var2, String var3);
<K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);
<T> Cursor<T> selectCursor(String var1);
<T> Cursor<T> selectCursor(String var1, Object var2);
<T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);
void select(String var1, Object var2, ResultHandler var3);
void select(String var1, ResultHandler var2);
void select(String var1, Object var2, RowBounds var3, ResultHandler var4);
int insert(String var1);
int insert(String var1, Object var2);
int update(String var1);
int update(String var1, Object var2);
int delete(String var1);
int delete(String var1, Object var2);
void commit();
void commit(boolean var1);
void rollback();
void rollback(boolean var1);
List<BatchResult> flushStatements();
void close();
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> var1);
Connection getConnection();
}
可以看到有很多方法,都是操作数据库的,包括执行sql语句和操作事务;这里其默认的实现类为DefaultSqlSession;需要注意的是: SqlSession不是线程安全的,所以需要在方法的内部使用,在执行sql语句前,使用openSession获得SqlSession对象;使用结束后,需要关闭它,执行sqlSession.close():
虽然发现Mybatis确实简化了很多JDBC的操作,但是按照上面的写法,还是有重复的部分,也就是
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
//通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
这段代码至少可以封装起来,之后就直接调用即可,所以这里创建一个工具类MybatisUtil类同时这里限制只创建一个SqlSessionFactory对象,所以可以参照JDBC工具类,使用static块
package cfeng.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory factory = null;
static {
String config = "mybatis.xml";
try {
InputStream in = Resources.getResourceAsStream(config);
//创建SqlSessionFactory对象
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession对象
public static SqlSession getSqiSession() {
SqlSession session = null;
if(factory != null) {
session = factory.openSession(false);
}
return session;
}
}
这里表明只是创建一次;只是执行一次的代码可以使用静态代码块的方式;单例模式也是只执行一次;
那么使用工具类,现在的myApp
package cfeng;
import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
import java.util.List;
public class myApp {
public static void main(String[] args) throws IOException {
SqlSession session = MybatisUtil.getSqiSession();
//指定要执行的sql语句,并执行
String sql = "cfeng.dao.StudentDao.selectStudents";
List<Student> list = session.selectList(sql);
//操作对象
list.forEach(System.out::println);
//list.forEach(stu -> System.our.println(out));
//关闭sql会话对象
session.close();
}
}
感觉没多大变化,但是最重要的一点就是彻底限制只创建一个factory对象;这样操作人员就没有创建多个占用资源的可能;Util最主要的作用就是static限制只创建一个factory
这里发现了一个问题,那就是创建的Dao接口没有起到任何的作用,因为只是给了xml文件一个参考,但是没有发挥任何的实际作用,以前Dao接口发挥作用是因为会写实现类,实现类中使用JDBC连接数据库进行操作,可是现在不需要dao的实体类了,那么Dao接口的意义?
这里使用mybatis配置的的文件要重复写有点麻烦,那也可以借助IDEA工具封装起来;如何封装?
File--->Settings--->File and Code Templates
然后点击+号,就可以在创建一个模板;比如这里创建一个mybatis的mapper文件模板
DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
<select id="" resultType="">
select>
mapper>
这样以后新建的时候就可以直接选择新建Mybatis-mapper文件,就有上面的内容了
同理,可以将主配置文件也设置为一个模板,名称就是Mybatis-config
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<environments default="myenv">
<environment id="myenv">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/***?servertimezone=GMT%2B8"/>
<property name="username" value=""/>
<property name="password" value=""/>
dataSource>
environment>
environments>
<mappers>
<mapper resource=""/>
mappers>
configuration>
这里就创建实现类即可;在Dao包下面创建子包Impl,然后创建实现类
package cfeng.dao.Impl;
import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> selectStudents() {
SqlSession session = MybatisUtil.getSqiSession();
//执行sql语句
String sql = "cfeng.dao.StudentDao.selectStudents";
List<Student> list = session.selectList(sql);
session.close();
return list;
}
@Override
public int insertStudent(Student student) {
SqlSession session = MybatisUtil.getSqiSession();
String sql = "cfeng.dao.StudentDao.insertStudent";
int count = session.insert(sql,student);
session.commit();
session.close();
return count;
}
}
这里就实现了方法;上面因为只是测试是否正常所以才没有使用Dao的Impl;测试就不需要单独创建测试类了;直接在Test下面进行测试
package cfeng;
import cfeng.dao.Impl.StudentDaoImpl;
import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import org.junit.Test;
import java.util.List;
public class TestStudentDaoImpl {
@Test
public void testSelectStudents(){
StudentDao dao = new StudentDaoImpl();
//dao对象,类型就是StudentDao,和namespace是相同的;而这个方法名称就是mapper文件中的id的值
//通过下面的方法调用就可以获得sql语句的定位
List<Student> list = dao.selectStudents();
list.forEach(System.out::println);
}
@Test
public void testInsertStudent(){
StudentDao dao = new StudentDaoImpl();
Student student = new Student(14,"新亚","HC2003");
int count = dao.insertStudent(student);
System.out.println(count);
}
}
非常方便就测试出dao的方法是正确的
分析Dao中的代码,发现前面的几行代码都是相同的,还有关闭等也是相同的;那么如何简化这种操作呢? 其中最核心的就是拿到sql语句,也就是通过namespace和id在mapper文件中获取到的sql语句
//dao对象,类型就是StudentDao,和namespace是相同的;而这个方法名称就是mapper文件中的id的值
//通过下面的方法调用就可以获得sql语句的定位 ---- 所以说这里的sql获取是可以简化的
Mybatis根据dao的方法调用,获取执行sql语句的信息; 所以之前强调namespace和id的命名必须和Dao的类全限定名和方法名一致,运作的方式就是Mybatis根据Dao接口,创建一个Dao接口的实现类,并创建这个类的对象,完成SqlSession调用方法,访问数据库
–其实就是
programmer不需要写上面的DaoImpl类,Mybatis通过JDK动态代理自动创建接口的实现类完成方法的调用;mybatis帮助写了DaoImpl类
强调namespace和id的命名必须和Dao的类全限定名和方法名一致!!!! ----- 不然无法通过反射获取到信息;所以现在不需要Impl了,直接删掉
//现在获得dao的方式
/**
* 使用mybatis的动态代理机制,使用session.getMapper(dao接口)
* getMapper能获取Dao接口对应的实现类对象,也就是DaoImpl的实例对象
*/
StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);
不需要创建对象了,动态代理已经创建好一个对象了,之后会手动给大家实现以下这个动态代理;【所以说这个也是Mybatis的一个重要的变革,那就是不再需要写实现类了,真的就只用在配置文件中写sql语句就可以了,最多再写一个接口】
可以再测试以下
package cfeng;
import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class TestStudentDaoImpl {
@Test
public void testSelectStudents(){
/**
* 使用mybatis的动态代理机制,使用session.getMapper(dao接口)
* getMapper能获取Dao接口对应的实现类对象,也就是DaoImpl的实例对象
*/
StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);
List<Student> list = dao.selectStudents();
list.forEach(System.out::println);
}
@Test
public void testInsertStudent(){
StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);
Student student = new Student(14,"新亚","HC2003");
int count = dao.insertStudent(student);
System.out.println(count);
}
}
但是工具类还是需要的,创建一个session
所以代理的就是接口中所有的功能,这个代理类就可以看作就是接口的实现类;所以前天的JDK动态代理,不需要工厂的,商店代理类其实主要代理的就是那个买雪糕的接口;emmm……JDK动态代理