目录
一、今日内容
二、框架的介绍
1、什么是框架
2、框架能解决什么问题
3、三层架构中常用的框架
三、mybatis框架的介绍
1、jdbc中的代码
创建数据库:
新建maven工程,导入junit和mysql驱动包
编写javabean和测试类
小结:原生jdbc流程:
2、jdbc代码中的问题
四、自定义mybatis框架
1、主要设计模式
2、流程图(注意下面一边对照流程图理解,一边搭环境)
3、具体编码
3.1新建项目
3.2 jar包和目录结构
3.3 第一部分:配置文件和对应的存储类
3.3 第二部分:SqlSession和实现类
3.4 第三部分:SqlSessionFactory
打成jar包以供使用:
五、使用自定义框架
六、使用mybatis框架(简单,重点)
简单mybatis框架使用小结(重点):
1、添加依赖(jar)
2、编写pojo对象
3、编写映射文件
4、编写核心配置文件
5、测试框架
1. 框架的介绍
2. 介绍mybatis框架
3. JDBC于Mybatis框架的比较
4. 自定义Mybatis框架(底层原理,难点)
5. mybatis框架的快速入门(简单,但重点掌握)
半成品
1、把技术封装起来
2、在开发中不应该考虑技术, 专注于逻辑(业务)开发
3、框架时位于底层技术 和应用程序之间的代码
a、web:表现层: 处理用户请求和响应
servlet -- struts(放弃) -- struts2(放弃 jd数据泄露事件) -- springMVC(主流)
b、service:业务层
自己写业务 --- ejb(过气) -- spring(整合框架,业务)(不可取代)
c、dao: 持久层(数据接收对象)
jdbc--dbutils--BeanUtils -- jdbcTemplate -- hibernate(放弃)--mybatis(主流)--spring data(简称jpa,持久层框架,未来趋势)
主流:整合: springmvc + spring + mybatis = ssm
趋势:整合:springmvc + spring +spring data = spring全家桶
换个工作空间就要重新配置一下maven...麻烦...尽量不要换(当练习也好,但不要经常)
create database mybatisdb_331;
use mybatisdb_331;
create table user(
id int primary key auto_increment,
username varchar(50),
password varchar(50),
sex varchar(2),
address varchar(50)
);
insert into user values(null,'zhangsan','zhangsan','男','蜀山');
insert into user values(null,'lisi','lisi','男','天墉城');
insert into user values(null,'wangwu','wangwu','男','琼华派');
select * from user;
删掉pom里对junit域仅test的限制:删掉
test
否则@Test在java包下用不了
User.java
package cn.ahpu.domain;
/**
* @author 韩竹安
* @create 2020-01-06 12:28
*/
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
//省略get/set
@Override
public String toString() {
return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}';
}
}
TestJDBC.java
package cn.ahpu;
import cn.ahpu.domain.User;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
*
* 回顾JDBC代码
*/
public class TestJDBC {
@Test
public void test() throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
String url="jdbc:mysql://localhost:3306/mybatisdb_331";
String username="root";
String password="root";
Connection conn = DriverManager.getConnection(url, username, password);
//3.sql语句
String sql="select * from user";
//4.创建statement对象 statement 和 PreparedStatement
PreparedStatement pst = conn.prepareStatement(sql);
//5.执行sql语句,返回结果集
ResultSet rs = pst.executeQuery();
//6.处理结果集
while(rs.next()){
User u = new User();
u.setId(rs.getInt("id"));
u.setUsername(rs.getString("username"));
u.setPassword(rs.getString("password"));
u.setAddress(rs.getString("address"));
u.setSex(rs.getString("sex"));
System.out.println("u = " + u);
}
//7.释放资源
rs.close();
pst.close();
conn.close();
}
}
原生的jdbc操作数据库流程:
1.注册驱动程序:Class.forName(“com.mysql.jdbc.Driver”);
2.使用驱动管理类来获取数据连接对象:conn = DriverManager.getConnection(…);
3.获取数据库操作对象:Statement stmt = conn.createStatement();
4.定义操作的SQL语句
5.执行SQL:stmt.executeQuery(sql);
6.处理结果集:ResultSet,如果SQL前有参数值就设置参数值setXXX()
7.关闭对象,回收数据库资源(关闭结果集–>关闭数据库操作对象–>关闭连接)
a、频繁创建和释放数据库的连接对象,造成资源的浪费. 使用数据库连接池可以解决(c3p0 ,dbcp, spring jdbc ,durid)
b、sql语句硬编码(写死),如果数据库发生改变,需要重新编译代码,再运行 。 可以考虑把sql语句写到配置文件中
c、传参数硬编码,必须按照特定的顺序传参
d、处理结果集硬编码,如果改变了数据库,结果集的映射必须重新写,需要重新编译代码,再运行
e、连接的配置信息硬编码
(难点,但仅仅为了了解底层原理,重在理解,不求会熟练地写)
工厂模式
什么是工厂模式
1.工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 2.在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,是通过使用一个共同的接口来指向新创建的对象。
栗子
您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
优缺点
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
构建者模式
什么是构建者模式
构建者模式一般用在构建流程或者组成部件固定的场合,将这些部件分开构建成为组件对象,再将这些组件对象整合成为目标对象。
栗子
组装台式电脑的情况,我们可以分别购买主板、CPU、内存、硬盘等部件,然后将这些部件组装在一起就形成了一台完整的电脑。
构建者模式和工厂模式的区别
工厂模式一般都是创建一个产品,注重的是把这个产品创建出来就行,只要创建出来,不关心这个产品的组成部分。从 代码上看,工厂模式就是一个方法,用这个方法就能生产出产品。 构建者模式也是创建一个产品,但是不仅要把这个产品创建出来,还要关心这个产品的组成细节,组成过程。从代码上看,建造者模式在建造产品时,这个产品有很多方法,建造者模式会根据这些相同方法但是不同执行顺序建造出不同组成细节的产品
maven配置默认即可
流程对了,也不用断网了
resources目录下新建目录:
UserMapper.xml
SqlMapConfig.xml
都不要约束(就是hibernate中的核心xml和单个javabean的xml) 注意一边对照流程图理解,一边搭环境
java目录下新建类:Configuration(对应核心配置文件SqlMapConfig.xml)
Configuration.java
package frame.domain;
public class Configuration {
//基本配置信息
private String url;
private String username;
private String password;
private String driverClass;
/**引入的一个个单个类的xml
* 第一个泛型String: 唯一标识:mapperId
* 第二个泛型Mapper: 存储sql+resultType
*/
private Map xmlMap=new HashMap();
//省略get/set
}
新建Mapper类
Mapper.java
package frame.domain;
public class Mapper {
private String sql;//执行的sql语句
private String resultType;//返回的结果集类型
//省略get/set
}
java目录下新建接口:frame.dao.SqlSession
SqlSession.java
package dao;
import java.util.List;
/**
* 框架入口,提供:增删改查方法
* 简单写一个框架,因此只设计一个查询所有的方法了
*/
public interface SqlSession {
/**
* 执行查询的方法
* mapperId 具体执行哪一条sql 唯一Id
* List:本来应该写泛型,为了简单就不写了
*/
public List selectList(String mapperId);
/**
* 释放资源
*/
public void close();
}
编写实现类,直接在接口上alter+enter
工具类:utils.Executor
开始有点乱序了,看视频复习最好
Executor.java(反射 难点)
package frame.utils;
import frame.domain.Configuration;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class Executor {
//想要执行肯定要加载驱动 获取连接 肯定需要配置信息 也就需要cfg
private Configuration cfg;
public Executor(Configuration cfg) {
this.cfg = cfg;
}
private Connection conn =null;
private PreparedStatement pst =null;
private ResultSet rs=null;
/**
* 在类名,属性列名都未知的情况下执行sql语句封装结果集
* driverClass,url,username,password
* @param sql
* @param resultType
* @return
*/
public List executeQuery(String sql, String resultType){
List list=new ArrayList();
//1.加载驱动类
try {
Class.forName(cfg.getDriverClass());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//2.获取连接
conn = DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
//3.创建statement对象
pst = conn.prepareStatement(sql);
//4.执行sql语句 返回结果集
rs = pst.executeQuery();
//5.处理结果集
//5.1 通过结果集获取所有列名
List columnNames=new ArrayList<>();
//获取结果集的元数据:修饰代码的代码 eg:注解
ResultSetMetaData metaData = rs.getMetaData();
//获取列的个数
int columnCount = metaData.getColumnCount();
//根据列索引得到列名 metaData.getColumnName(colum) 注意列的索引从1开始
for (int i = 1; i <= columnCount; i++) {//快捷 fori
String columnName = metaData.getColumnName(i);
columnNames.add(columnName);
}
//5.2 通过类全限定名创建对象
// 获取字节码文件
Class clz = Class.forName(resultType);
while(rs.next()){//判断是否有下一个元素 如有 就应该还有一个新的对象
//创建一个对象
//字节码通过反射创建对象
Object o = clz.newInstance();
//给对象赋值
//获取某列对应的set方法,执行set方法,进行赋值
//获取所有方法
Method[] methods = clz.getMethods();
for (Method method : methods) {//快捷:n=methods.for
for (String columnName : columnNames) {
//使用每一个列名与每一个方法比较(columnNames是rs得到的 methods是类全限定名得到的 可见javabean和表的列名要一致)
//setusername 忽略大小写进行比较 setUsername
if(("set"+columnName).equalsIgnoreCase(method.getName())){
//method就是columnName列对应的set方法了
//还需要获取属性对应的值
//通过列名获取列值
Object columValue = rs.getObject(columnName);
//执行set方法
//invoke方法2个参数:要执行的对象 方法的参数
//o对象执行method方法 参数为columValue
method.invoke(o,columValue);
}
}
}
//添加到返回的集合中
list.add(o);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public void close(){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst!=null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
SqlSessionImpl.java(简单调用)
package frame.dao.impl;
import frame.domain.Configuration;
import frame.domain.Mapper;
import frame.utils.Executor;
import java.util.List;
/**
* SqlSession实现类
*/
public class SqlSessionImpl implements dao.SqlSession {
//框架启动时会提前将xml配置文件信息全都读到Configuration类中
private Configuration cfg;
private Executor executor;
public SqlSessionImpl(Configuration cfg) {//唯一构造器 不给cfg参数不让创建对象
this.cfg = cfg;
executor=new Executor(cfg);//统一执行对象 方便close资源
}
//配置文件信息都已经在cfg中了(认为已经dom解析过了) 此处直接用cfg取内容即可
@Override
public List selectList(String mapperId) {
//根据mapperId获取sql语句和返回值类型
Mapper mapper = cfg.getXmlMap().get(mapperId);
String sql = mapper.getSql();
String resultType = mapper.getResultType();
return executor.executeQuery(sql,resultType);
}
@Override
public void close() {
executor.close();
}
}
frame包下新建类:
SqlSessionFactory.java
package frame.factory;
import frame.dao.impl.SqlSessionImpl;
import frame.domain.Configuration;
import frame.domain.Mapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
* 使用工厂模式创建SqlSession对象
* 主要是根据配置文件加载配置文件
*/
public class SqlSessionFactory {
//配置文件输入流(动态给出配置文件 位置+命名都不方便 直接给出文件流本身)
private InputStream inputStream;
public SqlSessionFactory(InputStream inputStream) {
this.inputStream = inputStream;
}
private Configuration cfg;
public dao.SqlSession openSession(){
cfg=new Configuration();
//给cfg赋值
loadConfiguration();
//创建sqlSession对象
SqlSessionImpl sqlSession = new SqlSessionImpl(cfg);
return sqlSession;
}
/**
* 读取配置文件SqlMapConfig.xml
*/
private void loadConfiguration() {
//dom4j 解析时,直接可以解析InputStream
//创建解析对象
SAXReader reader=new SAXReader();
Document doc = null;
try {
//获取文档对象:根据字节输入流获取文档对象
doc = reader.read(inputStream);
//获取根结点
Element root = doc.getRootElement();
//获取所有propert结点
List list = root.selectNodes("//property");
for (Element element : list) {
//获取指定属性的属性值
String value = element.attributeValue("value");
String name = element.attributeValue("name");
//判断是哪一个属性
if(name.equals("driver")){
cfg.setDriverClass(value);
}else if(name.equals("url")){
cfg.setUrl(value);
}else if(name.equals("username")){
cfg.setUsername(value);
}else if(name.equals("password")){
cfg.setPassword(value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
//读取关联的映射文件
//获取根结点对象
Element root = doc.getRootElement();
//得到mappers结点 就在第二层 于是一层层地读最简单
//element("name"):得到第一个name的子结点
//elements("name"):得到所有name的子结点
//elements():得到所有子结点
Element mappers = root.element("mappers");
List mapperList = mappers.elements("mapper");
for (Element element : mapperList) {
//获取单个类的xml路径
String path = element.attributeValue("resource");
loadXmlConfig(path);
}
}
/**
* 读取单个类的映射xml文件
*/
private void loadXmlConfig(String path){
//通过资源路径 获取资源的输入流对象
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);
//dom4j解析
SAXReader reader=new SAXReader();
try {
//获取文档对象
Document document = reader.read(inputStream);
//获取根结点
Element root = document.getRootElement();
//获取根结点namespace属性值
String namespace = root.attributeValue("namespace");
//获取根结点中所有子节点(防止有多个select)
List elements = root.elements();
//遍历每个结点 获取id resultType sql语句
for (Element element : elements) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String sql = element.getText().trim();//结点中的文本就是sql语句
String key=namespace+"."+id;//方法全限定名为key
Mapper value=new Mapper();
value.setSql(sql);
value.setResultType(resultType);
cfg.getXmlMap().put(key,value);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
SqlSessionFactoryBuider.java(简单高层调用api)
package frame.factory;
import java.io.InputStream;
/**
* @author 寒面银枪
* @create 2020-01-06 22:50
*
* 使用构建者模式创建SqlSessionFactory对象
* 简单高层调用api
*
*/
public class SqlSessionFactoryBuider {
/**
* 通过配合文件的输入流创建 SqlSessionFactory对象
* @param path
* @return
*/
public SqlSessionFactory build(String path){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);
return new SqlSessionFactory(inputStream);
}
/**
* 没有参数创建sqlsessionFactory对象
* 假设某位置(resources下)有sqlMapConfig
* @param path
* @return
*/
public SqlSessionFactory build(){
String path="SqlMapConfig.xml";//假设resources下有个配置文件SqlMapConfig.xml
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);
return new SqlSessionFactory(inputStream);
}
}
双击install即可打成jar包 并安装到本地仓库,直接双击install即可
注意:配置文件框架模型本身用不着,只为方便写代码的参考
自定义框架源码下载:mybatis_day01_2_customer_frame
---------------------------------------------------------------------至此自定义框架写好------------------------------------------------------------------------
pom.xml
User.java
package cn.ahpu.domain;
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
//省略get/set
@Override
public String toString() {
return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}';
}
}
上面写框架模板的工程中本不需要xml,可以直接删了 测试工程才需要
UserMapper.xml
SqlMapConfig.xml
AppTest.java 测试类内写测试方法
package cn.ahpu;
import static org.junit.Assert.assertTrue;
import cn.ahpu.domain.User;
import dao.SqlSession;
import frame.factory.SqlSessionFactory;
import frame.factory.SqlSessionFactoryBuider;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* Unit test for simple App.
*/
public class AppTest
{
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
@Test
public void testCustomerFrame(){
//0.获取配置文件输入流对象
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml");
//创建sqlSessionFactoryBuider对象
SqlSessionFactoryBuider sqlSessionFactoryBuider = new SqlSessionFactoryBuider();
//创建sqlSessionFactory对象
SqlSessionFactory sessionFactory = sqlSessionFactoryBuider.build(inputStream);
//获取sqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
//执行sql语句
List list = sqlSession.selectList("cn.ahpu.dao.UserDao.findAll");
//遍历结果集
for (User user : list) {
System.out.println(user);
}
//关闭资源
sqlSession.close();
}
}
仓库没有jaxen包也没事
自己写的那么好用,mybatis写的肯定更好用
注:
今日会向仓库导入(复制粘贴)600M+的jar包
需要在idea内更新下本地仓库
这个测试工程和上面的测试工程一模一样,除了配置文件要加上头约束外
其他代码都不用改(唯一尴尬的是之前代码的builder写成了buider少了个l,mybatis框架内肯定,没写错 要改下^_^)
mapper:
config:
mybatis包肯定不能用自己的了:
把自己的包删了,他会自动引入mybatis的包
junit
junit
4.12
test
org.mybatis
mybatis
3.4.5
mysql
mysql-connector-java
5.1.6
package cn.ahpu.domain;
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
//省略get/set
@Override
public String toString() {
return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}';
}
}
UserMapper.xml
SqlMapConfig.xml
AppTest.java
package cn.ahpu;
import cn.ahpu.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.assertTrue;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
@Test
public void testCustomerFrame(){
//0.获取配置文件输入流对象
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml");
//创建sqlSessionFactoryBuider对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//创建sqlSessionFactory对象
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//获取sqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
//执行sql语句
List list = sqlSession.selectList("cn.ahpu.dao.UserDao.findAll");
//遍历结果集
for (User user : list) {
System.out.println(user);
}
//关闭资源
sqlSession.close();
}
}