Mybatis哲学
来源:互联网 发布:淘宝商品降权3天恢复 编辑:程序博客网 时间:2024/05/22 17:05
MyBatis上层接口使用简单工厂模式,核心接口为org.apache.ibatis.session.SqlSession。其中定义了与数据相关的所有操作,以及和MyBatis本身相关的几个方法,比如getConfiguration和getMapper。下图列出了部分常用方法:
从上面的类图中可以看出,SqlSession已经覆盖了DAO模式中的所有方法。因为是ORM框架,所以都是包装方法。但如果在特殊情况下,一定要用JDBC原始java.sql.Connection接口的话,SqlSession中也提供了getConnection这个方法来获取。可以想象,这只有在很特别的情况下才会用到,因为此后,你对java.sql.Connection所做的任何操作都与MyBatis无关了,它提供的所有功能也因此不再有效,如事务等,都需要自己手动来管理了。
上面提到,MyBatis的上层核心接口是SqlSession,因此,上层的其它接口和类,均围绕它展开。下面从一个官方应用指南中的经典API例子开始,引出这几个重要的类。
点击(此处)折叠或打开
- String evironment="development";
- InputStream is = getClass().getResourceAsStream("/SqlMapperConfig.xml");
- SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
- SqlSessionFactory factory = factoryBulider.bulid(is,evironment);
- SqlSession session = factory.openSession();
- String selectId = "test.foo";
- Object bean = session.selectOne(selectdId);
- session.close;
- org.apache.ibatis.session.SqlSessionFactoryBuilder : SQL会话工厂的构建类
- org.apache.ibatis.session.SqlSessionFactroy : 打开一个SQL会话的工厂类
- org.apache.ibatis.session.SqlSession : SQL会话本身
- org.apache.ibatis.session.Configuration : 整个MyBatis的配置
点击(此处)折叠或打开
- public class SqlSessionFactoryBuilder {
- public SqlSessionFactory build(Readerreader){
- return build(reader,null,null);
- }
-
- public SqlSessionFactory build(Readerreader,Stringenvironment){
- return build(reader,environment,null);
- }
-
- public SqlSessionFactory build(Readerreader,Propertiesproperties){
- return build(reader,null,properties);
- }
-
- public SqlSessionFactory build(Readerreader,Stringenvironment,Propertiesproperties){
- try {
- XMLConfigBuilder parser = new XMLConfigBuilder(reader,environment,properties);
- return build(parser.parse());
- } catch(Exception e){
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally{
- ErrorContext.instance().reset();
- try {reader.close();catch(IOException e){//Intentionally ignore. Prefer previous error.}
- }
- }
- public SqlSessionFactory build(InputStreaminputStream){
- return build(inputStream,null,null);
- }
- public SqlSessionFactory build(InputStreaminputStream,Stringenvironment){
- return build(inputStream,environment,null);
- }
- public SqlSessionFactory build(InputStreaminputStream,Propertiesproperties){
- return build(inputStream,null,properties);
- }
- public SqlSessionFactory build(InputStreaminputStream,Stringenvironment,Propertiesprops){
- try {
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream,environment,props);
- return build(parser.parse());
- } catch(Exception e){
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally{
- ErrorContext.instance().reset();
- try{inputStream.close();}catch(IOException e){//Intentionally ignore.Prefer previous error.}
- }
- }
-
- public SqlSessionFactory build(Configurationconfig){
- return new DefaultSqlSessionFactory(config);
- }
- }
- 通过指定org.apache.ibais.session.Configuration来构建DefaultSqlSessionFactory
- 从一个Reader中加载Configuration,并构建SqlSessionFactory
- 从一个Stream中加载Configuration,并构建SqlSessionFactory
从SqlSessionFactoryBuilder中构建出来的工厂实现类,都是DefaultSqlSessionFactory,这是MyBatis默认的SqlSessionFactory接口的实现类,也是唯一一个真实的实现类,在本文的版本中(3.0.6),还有另外一个实现类:SqlSessionManager,但它并不是一个功能上的实现类,而是对SqlSession在实际应用开发过程中的实用工具类。因此,现在的重点落在了DefaultSqlSessionFactory类。SqlSessionFactory的功能是负责打开一个连接会话,该接口只定义了两个方法如下:
点击(此处)折叠或打开
- public interface SqlSessionFactory {
- SqlSession openSession();
- SqlSession openSession(boolean autoCommit);
- SqlSession openSession(Connectionconnection);
- SqlSession openSession(TransactionIsolationLevellevel);
- SqlSession openSession(ExecutorType execType);
- SqlSession openSession(ExecutorType execType,boolean autoCommit);
- SqlSession openSession(ExecutorType execType, TransactionIsolationLevellevel);
- SqlSession openSession(ExecutorType execType,Connectionconnection);
- Configuration getConfiguration();
- }
现在还剩下一个问题,前面提到的几个高层接口都找到了相应的实现类,那SqlSession呢?它的实现类是什么?当然你用Eclipse或是NetBeans这些高级IDE可以一下子找到,不过,我们这儿另辟溪径。我们知道,一个SqlSession的实例是这样得到的:SqlSessionFactoryBuilder.build(...).openSession(...);而build方法返回的是DefaultSqlSessionFactory实现类,查看一下这它的openSession方法不就知道了么。是的,如前段所述,最终我们可以在openSessionFromDataSource和openSessionFromConnection这两个方法中找到,得到的SqlSession实现类是org.apache.ibatis.session.defaults.DefaultSqlSession。再进入到该类中,在这儿,我们就快要接近真相(我们想知道,SqlSession的CRUD操作是如何实现的)了。其实,具体CRUD操作,都委托给了org.apache.ibaits.executors.Executor接口了。DefaultSqlSession只是为Executor提供执行参数和环境,是一个管理者,下面是它开头的部分源码:
点击(此处)折叠或打开
- public class DefaultSqlSession implements SqlSession{
- private Configurationconfiguration;
- private Executorexecutor;
- private boolean autoCommit;
- private booleandirty;
- public DefaultSqlSession(Configurationconfiguration,Executorexecutor,boolean autoCommit){
- this.configuration=configuration;
- this.executor=executor;
- this.autoCommit= autoCommit;
- this.dirty= false;
- }
- ....
- }
通过前面的分析,我们大致可以得到MyBatis一个粗略的上层架构类图:
作为一名一线应用开发人员,“配置”一词,可能已经听得耳朵都长茧了。但是,一个程序或者说是一个库,具有可配置性,是非常必要的,否则就得以纯编程的方式使用它们。试想一下,如果你在使用数据库产品时,你还需要通过编程来使用,那将是多么地糟糕!
配置不仅仅是使用的人要用,这个库或者框架的开发者自己也需要用,否则,如何组织内部的各个构件,将会是一件硬编码的事情。总之,配置,就是要组织出一个完整的逻辑或形式系统,以达到使用者的目的。在框架内部来说,通过配置,还在看清楚整个架构。
通过MyBatis的配置,可以看出整个框架的顶级特性。为什么这么说呢,把这些代码列出来就一目了然了。
点击(此处)折叠或打开
- public class Configuration {
- protected Environmentenvironment;
- protected boolean safeRowBoundsEnabled= true;
- protected boolean mapUnderscoreToCamelCase= false;
- protected boolean lazyLoadingEnabled= false;
- protected boolean aggressiveLazyLoading= true;
- protected boolean multipleResultSetsEnabled= true;
- protected boolean useGeneratedKeys= false;
- protected boolean useColumnLabel= true;
- protected boolean cacheEnabled= true;
- protected Integer defaultStatementTimeout;
- protected ExecutorType defaultExecutorType= ExecutorType.SIMPLE;
- protected AutoMappingBehavior autoMappingBehavior= AutoMappingBehavior.PARTIAL;
- protected Properties variables=newProperties();
- protected ObjectFactoryobjectFactory=new DefaultObjectFactory();
- protected ObjectWrapperFactory objectWrapperFactory=new DefaultObjectWrapperFactory();
- protected MapperRegistry mapperRegistry = new MapperRegistry(this);
- protected final InterceptorChain interceptorChain=new InterceptorChain();
- protected final TypeHandlerRegistry typeHandlerRegistry=new TypeHandlerRegistry();
- protected final TypeAliasRegistry typeAliasRegistry=new TypeAliasRegistry();
- protected finalMap<String, MappedStatement> mappedStatements= new StrictMap<String, MappedStatement>("Mapped Statements collection");
- protected finalMap<String, Cache> caches= new StrictMap<String, Cache>("Caches collection");
- protected finalMap<String, ResultMap> resultMaps= new StrictMap<String, ResultMap>("Result Maps collection");
- protected finalMap<String, ParameterMap> parameterMaps= new StrictMap<String, ParameterMap>("Parameter Maps collection");
- protected finalMap<String,KeyGenerator> keyGenerators= new StrictMap<String,KeyGenerator>("Key Generators collection");
- protected finalSet<String> loadedResources=newHashSet<String>();
- protected finalMap<String, XNode> sqlFragments= new StrictMap<String, XNode>("XML fragments parsed from previous mappers");
- protected finalCollection<XMLStatementBuilder> incompleteStatements=newLinkedList<XMLStatementBuilder>();
- protected finalCollection<CacheRefResolver> incompleteCacheRefs=newLinkedList<CacheRefResolver>();
- protected finalCollection<ResultMapResolver> incompleteResultMaps=newLinkedList<ResultMapResolver>();
- /**
- * A map holds cache-ref relationship. The key is the namespace that
- * references a cache bound to another namespace and the value is the
- * namespace which the actual cache is bound to.
- */
- protected finalMap<String,String> cacheRefMap= newHashMap<String,String>();
- public Configuration(Environmentenvironment){
- this();
- this.environment=environment;
- }
- public Configuration(){
- typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class.getName());
- typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class.getName());
- typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class.getName());
- typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class.getName());
- typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class.getName());
- typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class.getName());
- typeAliasRegistry.registerAlias("FIFO", FifoCache.class.getName());
- typeAliasRegistry.registerAlias("LRU", LruCache.class.getName());
- typeAliasRegistry.registerAlias("SOFT", SoftCache.class.getName());
- typeAliasRegistry.registerAlias("WEAK", WeakCache.class.getName());
- }
- ......
- }
数据环境(包括数据源和事务)
是否启用严格的行绑定
是否启用下划线与驼峰式命名规则的映射(如first_name => firstName)
是否启用懒加载模式
是否启用贪婪地懒加载模式(即尽可能多地使用懒加载)
是否启用多结果集映射
是否启用主键生成功能
是否启用采用列标签功能(如果不启用,则使用下标索引)
是否启用缓存功能
默认的JDBC语句超时设置
默认的执行器类型(共有SIMPLE,REUSE和BATCH三种)
初始化SqlMapping自动映射行为(共有NONE,PARTIAL和FULL三种)
其它文本属性(作为扩展或使用者自己用,存放在Promerties中)
初始化生成对象(实例)的工厂
初始化对象的包装工厂(用于代理,完成一些重要的特性,比如事务)
初始化SqlMapping的映射器
初始化拦截器链
初始化类型处理注册器(默认的注册器就已经预注册了常见的类型)
初始化类型别名注册器
初始化JDBC语句容器(就是一个Map,下同)
初始化缓存
初始化结果集容器
初始化参数容器
初始化主键生成器容器
初始化化已经加的载资源容器
初始化SQL语句片断容器(SQL语句片断,是可重用的,相信大家在Mapping文件中经常用到)
初始化不完整的JDBC语句容器(显然这个语句还没有执行插值操作)
初始化不完整的缓存引用容器
初始化不完整的结果集映射容器
初始化缓存引用容器
----------------------------------------------------
Configuration默认有两个构造器,一个是无参的,另一个则需要指定数据环境。但指定数据环境的这个构造器首先调用了无参的构造器。通过上面的源码,我们可以看到,无参构造器主要是注册了一些重要的类型别名,这些别名在XML配置中会用到。整个Configuration的所有字段都已经初始化了,除了environmnet。因此,它还提供一个传递数据环境的构造器。
Configuration是所有组件的有机组合器,同时也是运行时数据收集器,它会在DefaultSqlSession中用到,并再次传递给Executor接口,它们都依赖它。可以说它是拼接整个MyBatis应用的核心人物,就像Spring在应用程序开发中的地位。
summary:
MyBatis3中实现一对多的查询比较简单,可以自动完成。但插入操作要复杂一些,需要相关的DAO配合完成,这点不如Hibernate。
场景描述:
类:Mail和Attachment类
关系描述:一封邮件(Mail)可以有0个或多个附件(Attachment),附件(Attachment)仅对应一封邮件。
表格:mail表(主键:id_mail)和attachment表(外键:id_mail)。
- public class Mail implements Serializable {
- private static final long serialVersionUID = 7427977743354005783L;
- private Integer id;
- private String sender;
- private String subject;
- private String content;
- private String fromAddress;
- ...
- getters and setters...
- }
- public class Attachment implements Serializable {
- private static final long serialVersionUID = -1863183546552222728L;
- private String id;
- private String mailId;
- private String name;
- private String relativePath;
- ...
- getters and setters...
- }
- <?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">
- <configuration>
- <properties resource="test/properties/mysql.properties"></properties>
- <typeAliases>
- <typeAlias type="test.model.Mail" alias="Mail"/>
- <typeAlias type="test.model.Attachment" alias="Attachment"/>
- </typeAliases>
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC" />
- <dataSource type="UNPOOLED">
- <property name="driver" value="${db_driver}" />
- <property name="url" value="${db_url}" />
- <property name="username" value="${db_user}" />
- <property name="password" value="${db_password}"/>
- </dataSource>
- </environment>
- </environments>
- <mappers>
- <mapper resource="test/data/MailMapper.xml"/>
- <mapper resource="test/data/AttachmentMapper.xml"/>
- </mappers>
- </configuration>
- <?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">
- <mapper namespace="test.data.MailMapper">
- <cache />
- <resultMap type="Mail" id="result_base">
- <id property="id" column="id_mail" />
- <result property="sender" column="sender"/>
- <result property="fromAddress" column="from_address" />
- <result property="subject" column="subject"/>
- <result property="content" column="content"/>
- <result property="sendTime" column="send_time" />
- ....
- </resultMap>
- <!--这里是关键,一对多映射的“魔法”几乎都在<collection>的配置里。select=...中"test.data.AttachmentMapper"对应于AttachmentMapper中
- 的namespace-->
- <resultMap type="Mail" id="result" extends="result_base">
- <collection property="attachments" javaType="ArrayList" column="id_mail" ofType="Attachment"
- select="test.data.AttachmentMapper.selectByMailId"/>
- </resultMap>
- <insert id="insert" parameterType="Mail" useGeneratedKeys="true" keyProperty="id_note">
- insert into note(sender, from_address, subject, content, send_time)
- values(#{sender}, #{fromAddress}, #{subject}, #{content}, #{sendTime})
- <selectKey keyProperty="id_mail" resultType="int">
- select LAST_INSERT_ID()
- </selectKey>
- </insert>
- <select id="selectById" parameterType="int" resultMap="result" >
- select * from mail where id_mail = #{id}
- </select>
- <select id="selectAllMails" resultMap="result">
- select * from note Note
- </select>
- <!--这里可以获得刚插入表格的id,为后面attachment的插入提供了mailId字段-->
- <select id="selectLastId" resultType="int">
- select LAST_INSERT_ID()
- </select>
- </mapper>
- <?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">
- <mapper namespace="test.data.AttachmentMapper">
- <cache />
- <resultMap type="Attachment" id="result">
- <result property="id" column="id_accessory" />
- <result property="name" column="name" />
- <result property="relativePath" column="relative_path" />
- <result property="mailId" column="id_mail" />
- </resultMap>
- <insert id="insert" parameterType="Attachment">
- insert into attachments(id_mail, name, relative_path) values(#{mailId}, #{name}, #{relativePath})
- </insert>
- <!--MailMapper中的ResultMap调用这个方法来进行关联-->
- <select id="selectByMailId" parameterType="int" resultMap="result">
- select id, id_mail, name, relative_path
- from attachments where id_note = #{id}
- </select>
- </mapper>
DAO
- public class AttachmentDAO {
- private SqlSessionFactory sqlSessionFactory;
- public AttachmentDAO(){
- this.sqlSessionFactory = TestConnectionFactory.getSqlSessionFactory();
- }
- public void insert(Attachment attachment){
- SqlSession session = sqlSessionFactory.openSession();
- AttachmentMapper attachmentMapper = session.getMapper(AttachmentMapper.class);
- try {
- attachmentMapper.insert(attachment);
- session.commit();
- } finally {
- session.close();
- }
- }
- }
- public class MailDAO {
- private SqlSessionFactory sqlSessionFactory;
- public MailDAO(){
- sqlSessionFactory = TestConnectionFactory.getSqlSessionFactory();
- }
- public void insertMailOnly(Mail mail){
- SqlSession session = sqlSessionFactory.openSession();
- MailMapper mailMapper = session.getMapper(MailMapper.class);
- try {
- mailMapper.insert(mail);
- session.commit();
- } finally {
- session.close();
- }
- }
- //inset
- public void insertMail(Mail mail){
- SqlSession session = sqlSessionFactory.openSession();
- MailMapper mailMapper = session.getMapper(MailMapper.class);
- AttachmentMapper attachmentMapper = session.getMapper(AttachmentMapper.class);
- try{
- mailMapper.insert(mail);
- //这里必须commit,再执行Attachment的插入操作。否则会导致null pointer异常
- session.commit();
- //获得最近插入到note表的id
- int mailId = mailMapper.selectLastId();
- for(Attachment attach : mail.getAttachments()){
- attach.setMailId(String.valueOf(mailId));
- attachmentMapper.insert(attach);
- }
- session.commit();
- }finally{
- session.close();
- }
- }
- public ArrayList<Mail> selectAllMails(){
- ArrayList<Mail> mailList = null;
- SqlSession session = sqlSessionFactory.openSession();
- MailMapper mailMapper = session.getMapper(MailMapper.class);
- try {
- mailList = mailMapper.selectAllMails();
- session.commit();
- } finally {
- session.close();
- }
- urn mailList;
- }
- public Mail selectMailById(int i){
- Mail mail = null;
- SqlSession session = sqlSessionFactory.openSession();
- MailMapper mailMapper = session.getMapper(MailMapper.class);
- try {
- mail = mailMapper.selectById(i);
- session.commit();
- } finally {
- session.close();
- }
- urn mail;
- }
- public int selectLastId(){
- int id = -1;
- SqlSession session = sqlSessionFactory.openSession();
- MailMapper mailMapper = session.getMapper(MailMapper.class);
- try {
- id = mailMapper.selectLastId();
- session.commit();
- } finally {
- session.close();
- }
- urn id;
- }
- Mybatis哲学
- 哲学
- 哲学
- 哲学
- 哲学
- MyBatis Generator(MBG)设计哲学与致歉
- 豺狼哲学
- 豺狼哲学
- 关于哲学
- ...成功哲学...
- 现代哲学
- 哲学问题
- Unix哲学
- 哲学随感
- 成功哲学
- Unix哲学
- 《人工智能哲学》
- 爱情哲学
- Mybatis源码
- mapreduce流程1
- Android系统学习1
- eclipse 快捷键
- hibernate修改数据变插入
- Mybatis哲学
- C#委托基础6——泛型委托Predicate
- mapreduce流程2
- 各种距离
- 341. One eyewitness is better than ten hearsays.百闻不如一见
- MAPREDUCE流程3
- ZOJ 3675 Trim the Nails 小水题
- 生活,只愿平静
- SQL由入门到精通的学习