Java总复习(四)——MyBatis、Thymeleaf、Maven

MyBatis的简单实现

Maper的构建过程

SqlSession基本构建的代码为:

String resource = "mybatis-config.xml";//设置mybatis配置文件的资源路径(类路径下)
InputStream inputStream = null;
try {
	inputStream = Resources.getResourceAsStream(resource);//获取配置文件的输入流
} catch (IOException e) {
	e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactoryBuilder的build方法,将输入流中的信息进行配置,返回一个SqlSessionFactory对象
SqlSession session = sqlSessionFactory.openSession();
  1. 获取SqlSessionFactory:
    在这里插入图片描述
    关键就在于mybatis使用SqlSessionFacoty来获取SqlSession对象,SqlSessionFactory是一个接口,默认使用的实现是DefaultSqlSessionFactory,在创建DefaultSqlSessionFactory时将xml配置文件解析后封装的Configuration对象传入,就得到了该配置文件所对应的SqlSessionFactory
  2. 获取SqlSession:
    在这里插入图片描述
  3. 获取Mapper:
    在获取Mapper时传入参数为编写Dao接口的class,而mybatis能够返回一个类对象,显然是使用了动态代理

简单实现

MyBatis内部逻辑比这要复杂得多,这里简单实现mybatis工作流程

配置文件Dom类

  1. MyConfiguration
/**
 * mybatis-config配置文件对应的dom对象
 * @author lenovo
 *
 */
public class MyConfiguration {

	//数据库连接信息
	private String url;
	private String driver;
	private String username;
	private String password;
	//映射文件信息,键使用namespace+id保证唯一性,值使用一个MyMappedStatement对象
	private Map<String,MyMappedStatement> mappers = new HashMap<>();
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getDriver() {
		return driver;
	}
	public void setDriver(String driver) {
		this.driver = driver;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public Map<String,MyMappedStatement> getMappers() {
		return mappers;
	}
	public void setMappers(Map<String,MyMappedStatement> mappers) {
		this.mappers = mappers;
	}
	@Override
	public String toString() {
		return "MyConfiguration [url=" + url + ", driver=" + driver + ", username=" + username + ", password="
				+ password + ", mappers=" + mappers + "]";
	}
	
}
  1. MyMappedStatement
/**
 * mapper映射文件dom对象
 * @author lenovo
 *
 */
public class MyMappedStatement {

	private String namespace;
	private String id;
	private String resultType;
	private String sql;
	public String getNamespace() {
		return namespace;
	}
	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getResultType() {
		return resultType;
	}
	public void setResultType(String resultType) {
		this.resultType = resultType;
	}
	public String getSql() {
		return sql;
	}
	public void setSql(String sql) {
		this.sql = sql;
	}
}

SqlSessionFactory的构建

  1. MySqlSessionFactory
/**
 * sqlsession工厂,用于获取SqlSession对象
 * @author shaofan
 *
 */
public interface MySqlSessionFactory {
	
	MySqlSession openSession();
}

  1. MyDefaultSqlSessionFactory,SqlSessionFactory的默认实现,这里进行mybatis配置文件的读取
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * SqlSessionFactory的默认实现
 * 
 * @author shaofan
 *
 */
public class MyDefaultSqlSessionFactory implements MySqlSessionFactory {

	private final MyConfiguration myConfiguration = new MyConfiguration();;
	private final SAXReader reader = new SAXReader();
	public MyDefaultSqlSessionFactory(String xmlPath) {
		Document document = null;
		try {
			//读取类路径下的配置文件
			document = reader.read(this.getClass().getResource(xmlPath));
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		// 加载数据库配置文件
		loadDBInfo(document);
		// 加载映射文件
		loadMapperInfo(document);
	}

	private void loadDBInfo(Document document) {
		
		Element element = document.getRootElement();
		for (Element e : element.element("dataSource").elements("property")) {
			try {
				//通过标签name属性找到对应的配置字段并调用set方法将配置存入对象中
				Method method = MyConfiguration.class.getDeclaredMethod("set"
			+e.attributeValue("name").substring(0, 1).toUpperCase()
			+e.attributeValue("name").substring(1),String.class);
				method.invoke(myConfiguration, e.attributeValue("value"));
			} catch (NoSuchMethodException e1) {
				e1.printStackTrace();
			} catch (SecurityException e1) {
				e1.printStackTrace();
			} catch (IllegalAccessException e1) {
				e1.printStackTrace();
			} catch (IllegalArgumentException e1) {
				e1.printStackTrace();
			} catch (InvocationTargetException e1) {
				e1.printStackTrace();
			}
		}
	}

	private void loadMapperInfo(Document document) {
		Element root = document.getRootElement();
		//遍历mapper标签
		for(Element e:root.element("mappers").elements("mapper")) {
			//通过配置的映射文件路径加载映射文件
			URL mapperPath = this.getClass().getResource(e.attributeValue("resource"));
			Document mapperDocument = null;
			try {
				mapperDocument = reader.read(mapperPath);
			} catch (DocumentException e1) {
				e1.printStackTrace();
			}
			Element mapperRoot = mapperDocument.getRootElement();
			MyMappedStatement myMappedStatement = new MyMappedStatement();
			myMappedStatement.setNamespace(mapperRoot.attributeValue("namespace"));
			for(Element ele:mapperRoot.elements()) {
				myMappedStatement.setId(ele.attributeValue("id"));
				myMappedStatement.setResultType(ele.attributeValue("resultType"));
				myMappedStatement.setSql(ele.getData().toString());
				//当出现重复的sql配置时抛出异常
				if(myConfiguration.getMappers().containsKey(myMappedStatement.getNamespace()+"."+myMappedStatement.getId())) {
					throw new RuntimeException("重复的方法");
				}
				myConfiguration.getMappers().put
				(myMappedStatement.getNamespace()+"."+myMappedStatement.getId(),
						myMappedStatement);
			}
		}
		System.out.println(myConfiguration);
	}

	@Override
	public MySqlSession openSession() {
		MySimpleExecutor executor = new MySimpleExecutor(myConfiguration);
		return new MyDefaultSqlSession(myConfiguration, executor);
	}

}

SqlSessionFactory的构建

  1. MySqlSession
/**
 * SqlSession对象,用于获取Dao对象
 * @author shaofan
 *
 */
public interface MySqlSession {

	<T> T getMapper(Class<T> c) throws InstantiationException, IllegalAccessException;
}
  1. MyDefaultSqlSession,MySqlSession的默认实现
public class MyDefaultSqlSession implements MySqlSession{

	private final MyConfiguration configuration;
	private final MyExecutor executor;
	
	public MyDefaultSqlSession(MyConfiguration configuration,MyExecutor executor) {
		this.configuration = configuration;
		this.executor = executor;
	}
	@Override
	public <T> T getMapper(Class<T> c) throws InstantiationException, IllegalAccessException {
		return MyAspect.getProxy(c,executor,configuration);
	}

}
  1. MyAspect,代理切面,这里使用cglib的动态代理,由于Dao接口没有实现类,无法使用jdk动态代理
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyAspect{
	
	public static <T>T  getProxy(Class<T> c,MyExecutor executor,MyConfiguration configuration) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(c);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				String key = method.getDeclaringClass().getName() + "." + method.getName();
				return executor.execute(configuration.getMappers().get(key), args);
			}
		});
		return (T)enhancer.create();
	}

}

Executor

  1. MyExecutor
public interface MyExecutor {

	Object execute(MyMappedStatement ms,Object parameter);
}
  1. MySimpleExecutor,MyExecutor的简单实现,解析Sql比较复杂,这里不做实现
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MySimpleExecutor implements MyExecutor{
	
	private Connection conn;
	
	
	public MySimpleExecutor(MyConfiguration configuration) {
		try {
			Class.forName(configuration.getDriver());
			conn = DriverManager.getConnection(configuration.getUrl(),configuration.getUsername(),configuration.getPassword());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		System.out.println("连接数据库成功");
	}

	@Override
	public Object execute(MyMappedStatement ms, Object parameter) {
		System.out.println("开始执行sql");
		String type = ms.getSql().substring(0,ms.getSql().indexOf(' '));
		if(type.contains("select")) {
			doQuery(ms,parameter);
		}else {
			doUpdate(ms,parameter);
		}
		return null;
	}
	
	private <T> void doUpdate(MyMappedStatement ms, T parameter) {
		String sql = ms.getSql();
		System.out.println(sql);
	}

	private void doQuery(MyMappedStatement ms, Object parameter) {
		String sql = ms.getSql();
		System.out.println(sql);
		
	}	

}

MyBatis插件原理

MyBatis 的插件可以在不修改原来的代码的情况下,通过拦截的方式,改变四大核心对象的行为,比如处理参数,处理SQL,处理结果
在这里插入图片描述

Executor增强流程

在这里插入图片描述
在这里插入图片描述

MyBatis经典面试题

  1. MyBatis有什么优点?

免除了jdbc代码以及设置参数和结果集操作,简化了数据库访问操作
将对象与关系进行映射,省去了手动封装结果集的操作
MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的维护带来了很大便利
相对于Mybatis-plus和hibernate这样的全自动orm映射,mybatis能够结合数据库特点灵活控制sql,在一些场景下效率更高,能够完成复杂场景的查询

  1. 写的数据访问对象(Dao)是一个接口,MyBatis是如何给我们返回一个对象的?

MyBatis的SqlSession通过动态代理的方式,给我们返回一个增强对象,我们在调用方法的时候,被代理的拦截器所拦截,然后通过方法名找到MappedStatement中对应的Sql交由Executor来执行

  1. #{}和${}的区别

#{}会进行预处理操作,${}不会进行

  1. 一级缓存和二级缓存的区别

一级缓存存在于SqlSession,是默认开启的,同一个SqlSession操作相同sql会从缓存中读取
二级缓存存在于SqlSessionFactory,默认不开启,不同SqlSession操作相同sql也会从缓存中读取(二级缓存的开启使用第三方如EHCache资源来提供缓存)

  1. MyBatis常用的注解有哪些?

@Select、@Update等映射增删改查的sql语句
@SelectProvider、@UpdateProvider等映射删改查的动态Sql
@Results映射结果集的列表(对应xml文件中的resultMapper标签)
@One、@Many用于多对一、一对多查询,对应xml文件中的association、collection标签
@Param用于传入多个参数时,为参数取名
@Options用于指定附加属性配置,比如useGeneratedKeys、fetchType

  1. MyBatis分页插件的原理

分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

  1. 如何理解mybatis-plus

概念:MP是Mybatis的一个增强工具,它简化了MyBatis大量的编写sql的操作
优点:Mybatis-Plus是在Mybatis的基础上做了增强,却不做改变;使用Mybatis-Plus除了能简化开发,在复杂场景下又保留了mybatis原生功能
缺点(注意事项):Mybatis-plus虽然简化了大量开发,但容易造成代码层次混乱,我们可能把大量数据逻辑写道service甚至是controller中,所以使用的时候一定要注意分析,不要把所有数据操作都交给MP去实现
如何配置:Springboot项目中,在配置文件中将PO和数据表的映射关系配置好后,使Dao继承自BaseMapper即可

Thymeleaf

  1. 使用Thymeleaf有什么优点?(相对于JSP)

Thymeleaf能够于HTML语言兼容
Thymeleaf适合做静态化

  1. 如何理解静态化

概念:静态化是指通过程序将模板或数据生成为静态页面
优点:访问静态页面不用访问数据库,提高性能
缺点:数据的不具有时效性,用户看到的消息可能已经过时
如何实现静态化:使用nginx进行动静分离,将静态资源放在nginx服务器下

  1. 常见的Thymeleaf指令

th:text替换文本,th:each循环集合数据,th:if执行判断

Maven

  1. Maven的生命周期

Maven有三个独立的生命周期,分别为clean:清理项目、default:构建项目、site:建立和发布项目站点;每个生命周期方法执行的时候,会把当前生命周期的前面所有方法一次执行

  1. 如何管理多模块的项目依赖版本?

通过继承的方式,在父模块中声明<dependencyManagement /><pluginManagement />,然后让子模块通过元素指定父模块,这样子模块中定义以来就可以只定义groupId和artifactId,自动使用父模块的version
通过聚合的方式,使用<dependency />时声明scope为import,从而引入一个pom的<dependencyManagement ./>

  1. Maven聚合与继承的区别

聚合是在父模块中配置module标签来指定子模块,接下来所有操作在父模块中进行可以对子模块做一并的操作
继承是在子模块使用parent标签来指定父模块,父模块的依赖子模块也会有,避免重复的依赖导入代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值