mybatis
1. what is mybatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2. how to use mybatis
1. 编程式
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可。
org.mybatis
mybatis
x.x.x
2. 集成式
1. MyBatis-Spring
要使用 MyBatis-Spring 模块,你只需要包含 mybatis-spring-x.x.x.jar 文 件就可以了,并在类路径中加入相关的依赖
SqlSessionFactoryBean MapperFactoryBean org.mybatis mybatis-spring x.x.x 一个使用 MyBatis-Spring 的主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而 不是给 MyBatis 创建一个新的特定的事务管理器,MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager。 一旦 Spring 的 PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法来配置事务。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面编程,译 者注)样式的配置都是支持的。在事务处理期间,一个单独的 SqlSession 对象将会被创建 和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。 一旦事务创建之后,MyBatis-Spring 将会透明的管理事务。 SqlSessionTemplate SqlSessionTemplate 是 MyBatis-Spring 的核心。 这个类负责管理 MyBatis 的 SqlSession, 调用 MyBatis 的 SQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。 MapperScannerConfigurer
2. mybatis/spring-boot-starter
@MapperScan("com.neo.mapper") 或者直接在Mapper类上面添加注解@Mapper @ImportResource({"classpath:config/spring/spring_*.xml"}) mybatis.mapperLocations=classpath*:**/mappers/*.xml org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.2
3. mybatis framework
1. Resources
String resource = "file:///C:\\Users\\banma\\Desktop\\academy\\demo\\src\\main\\resources\\static\\pom.xml"; String resource = "https://maven.apache.org/what-is-maven.html"; InputStream inputStream = Resources.getUrlAsStream(resource); String resource = "static/pom.xml"; InputStream inputStream = Resources.getResourceAsStream(resource);
2. Configuration
public class SqlSessionFactoryBuilder public SqlSessionFactory build(Configuration config) {new DefaultSqlSessionFactory(config);} public class DefaultSqlSessionFactory implements SqlSessionFactory public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);} Configuration Environment environment {id transactionFactory dataSource} LocalCacheScope localCacheScope = LocalCacheScope.SESSION; //一级缓存 session 级别SESSION,STATEMENT ExecutorType defaultExecutorType = ExecutorType.SIMPLE;//SIMPLE, REUSE, BATCH reflectorFactory // model mapperRegistry {config Map
, MapperProxyFactory>> knownMappers = new HashMap , MapperProxyFactory>>();} //class factory interface mappedStatements = new StrictMap ("Mapped Statements collection"); //xml node namespace collection resultMaps = new StrictMap ("Result Maps collection"); parameterMaps = new StrictMap ("Parameter Maps collection"); keyGenerators = new StrictMap ("Key Generators collection"); Set loadedResources = new HashSet ();// xml namespace interface sqlFragments = new StrictMap ("XML fragments parsed from previous mappers"); interceptorChain = new InterceptorChain(); typeHandlerRegistry = new TypeHandlerRegistry(); typeAliasRegistry = new TypeAliasRegistry();
3. Connection
public interface javax.sql.DataSource Connection getConnection() public interface java.sql.Connection java.sql.Statement connection.prepareStatement(sql, keyColumnNames) public interface java.sql.PreparedStatement extends Statement
4. SqlSession
Resources InputStream SqlSessionFactoryBuilder SqlSessionFactory openSession() DefaultSqlSessionFactory / SqlSessionManager SqlSession -> DefaultSqlSession ResultHandler -> DefaultMapResultHandler ResultContext -> DefaultResultContext MapperRegistry MapperProxyFactory MappedStatement MapperProxyFactory TypeHandler -> IntegerTypeHandler / BaseTypeHandler
Plugin Executor ExecutorType Interceptor InterceptorChain SqlSession public interface org.apache.ibatis.session.SqlSession extends Closeable // insert delete update select // commit rollback close // getConfiguration() getMapper() getConnection() public class DefaultSqlSession implements SqlSession configuration executor public interface org.apache.ibatis.executor.Executor public abstract class BaseExecutor implements Executor protected Transaction transaction; protected Executor wrapper; protected Configuration configuration; public class CachingExecutor implements Executor Executor delegate tcm = new TransactionalCacheManager(); public interface Transaction protected java.sql.Connection connection; protected javax.sql.DataSource dataSource; protected TransactionIsolationLevel level;
5. Proxy
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); MapperProxyFactory final MapperProxy
mapperProxy = new MapperProxy (sqlSession, mapperInterface, methodCache); (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); public class MapperProxyFactory private final Class mapperInterface; private final Map methodCache = new ConcurrentHashMap (); public class MapperProxy implements InvocationHandler, Serializable private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public Object invoke(Object proxy, Method method, Object[] args) result = sqlSession.selectOne(command.getName(), param); SimpleExecutor DataSource PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler. handleResultSets(ps);
4. source code
1. public class MapperMethod
private final SqlCommand command;
private final MethodSignature method;
2. public enum SqlCommandType
{ UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;}
5. issues
1. n+1 问题
n+1次查询 懒加载 (部分缓解 ) 1+n 问题 join 查询主数据,是1次查询,查询出n条记录;根据这n条主记录,查询从记录,共需要n次,所以叫数据库1+n问题 N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的: 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。 这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。 MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。
2. 结果嵌套 查询嵌套
3. 缓存
一级缓存 二级缓存 Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。 每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询语句本身都会被保存在本地缓存中,那么,相同的查询语句和相同的参数所产生的更改就不会二度影响数据库了。本地缓存会被增删改、提交事务、关闭事务以及关闭 session 所清空。 默认情况下,本地缓存数据可在整个 session 的周期内使用,这一缓存需要被用来解决循环引用错误和加快重复嵌套查询的速度,所以它可以不被禁用掉,但是你可以设置 localCacheScope=STATEMENT 表示缓存仅在语句执行时有效。 注意,如果 localCacheScope 被设置为 SESSION,那么 MyBatis 所返回的引用将传递给保存在本地缓存里的相同对象。对返回的对象(例如 list)做出任何更新将会影响本地缓存的内容,进而影响存活在 session 生命周期中的缓存所返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。 MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。 默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
映射语句文件中的所有 select 语句将会被缓存。 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.
4. # $
# 创建一个预处理语句参数
5. XML 映射文件 使用 Java 注解
命名空间(Namespaces)
6. 作用域(Scope)和生命周期
SqlSessionFactoryBuilder 方法作用域(也就是局部方法变量) SqlSessionFactory 最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式 SqlSession 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 映射器实例(Mapper Instances) 映射器是一个你创建来绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求作用域(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法作用域(method scope)内。
7. 动态 SQL
if choose (when, otherwise) trim (where, set) foreach bind
8. Java API
每个映射器方法签名应该匹配相关联的 SqlSession 方法,而字符串参数 ID 无需匹配。相反,方法名必须匹配映射语句的 ID。 此外,返回类型必须匹配期望的结果类型,单返回值时为所指定类的值,多返回值时为数组或集合。所有常用的类型都是支持的,包括:原生类 型、Map、POJO 和 JavaBean 你可以传递多个参数给一个映射器方法。如果你这样做了,默认情况下它们将会以 "param" 字符串紧跟着它们在参数列表中的位置来命名,比如:#{param1}、#{param2}等。如果你想改变参数的名称(只在多参数情况下),那么你可以在参数上使用 @Param("paramName") 注解。 你也可以给方法传递一个 RowBounds 实例来限制查询结果。
9. 日志
10. generator
org.mybatis.generator mybatis-generator-maven-plugin 1.3.5 mysql mysql-connector-java 5.1.39 ${basedir}/src/main/resources/generatorConfig.xml true