《Spring+Spring MVC+MyBatis从零开始学》傻瓜式学习笔记

前言:买这本书主要是为了学MyBatis。所以着重记一下第六章以及后面的。
目的:学习大量开发知识,早日跳槽去更好的单位。[2019年09月02日]

错误更改:第六章 6.2.3 第73页 示例6-4 中第14行代码:

int rows = sqlSession.update("com.ssm.mapper.UserMapper.addUser", user);

应该修改为

int rows = sqlSession.update("com.ssm.mapper.UserMapper.updateUser", user);

笔记一[2019年08月30日]

第7章第一页的7.1.1第一段话对于我来说有点复杂,所以我把它列出来配合网上的搜索深刻理解一下:

SqlSessionFactory 是单个数据库映射关系经过编译后的内存镜像,用于创建SqlSession。SqlSessionFactory对象的实例通过SqlSessionFactoryBuilder对象来构建,通过XML配置文件,或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。

数据库的映射关系:

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。

1.先来看第一个句子怎么解释:

SqlSessionFactory 是单个数据库映射关系经过编译后的内存镜像,用于创建SqlSession。

那么想要知道数据库的映射关系是什么就需要知道ORM是什么。
查一下网上的资料:什么是ORM?为啥要是用ORM?

了解orm,先了解以下概念:
什么是“持久化”
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
什么是 “持久层”
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
什么是ORM
即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

这下有点明白了,大概是UserMapper.xml这类映射文件的缩影,目的就是数据持久化。
[注明:持久层只是一个抽象的形容词而已,代表这一类]

内存镜像:[与内存映像不同]

内存镜像是指将服务器上的内存分为两个频道。一个频道是另一个频道的镜像,用来创建内存的冗余副本。

并没有写是什么东西,是什么概念,继续查:

内存镜像就像硬盘存储的RAID 1。

所以搞清楚RAID 1就知道内存镜像是什么东西。

RAID 1通过磁盘数据镜像实现数据冗余,在成对的独立磁盘上产生互为备份的数据。

还是没写明白究竟是什么东西,继续查。

RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,通常简称为磁盘阵列。简单地说, RAID 是由多个独立的高性能磁盘驱动器组成的磁盘子系统,从而提供比单个磁盘更高的存储性能和数据冗余的技术

从这里可以看出来,RAID是一种技术,继续查了一下独立磁盘冗余阵列,网上也都说是一种技术:

磁盘阵列是由很多块独立的磁盘,组合成一个容量巨大的磁盘组,利用个别磁盘提供数据所产生加成效果提升整个磁盘系统效能。利用这项技术,将数据切割成许多区段,分别存放在各个硬盘上。

所以,回推回去,RAID 1是一种技术,那么内存镜像也就是一种类似于RAID 1的技术:将内存数据做两个拷贝,分别放在主内存和镜像内存中的一种技术。
所以,SqlSessionFactory就是ORM经过编译后的一种技术,用来创建SqlSession。从名字可以看出是工厂设计模式。

2.再来看第二个句子的前面短句概括部分:

SqlSessionFactory对象的实例通过SqlSessionFactoryBuilder对象来构建,

根据代码就可以充分了解:
是SqlSessionFactoryBuilder().build()而不是SqlSessionFactory().build()

// 2.根据配置文件构建SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

3.再来看最后面短句的解释:

通过XML配置文件,或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。

通过XML配置文件,我们可以理解,也就是.build(inputStream),inputStream则是配置文件的输入流:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

那预先定义好的Configuration实例,是什么,网上的资料:

使用java代码构建
SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来构
建,而SqlSessionFactoryBuilder则可以通过一个预先定义好的Configuration实例(java代码)构建出SqlSessionFactory的实例。
Mybatis全局配置文件元素的作用与用法

这里没有我们想要的答案,只知道是用Java代码写的不是xml文件,感觉这个比那个内存镜像还要难理解,继续寻找:

最后一个 build 方法使用了一个 Configuration 实例。configuration 类包含可能需要了解 SqlSessionFactory 实例的所有内容。Configuration 类对于配置的自查很有用,包含查找和 操作 SQL 映射。手动配置 configuration 实例,然后将它传递给 build()方法来创建 SqlSessionFactory。
MyBatis学习总结(二)——SQLSessionFactory实例

部分代码如下:

DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);

这下完全懂了,就是先配置好configuration实例,直接砸进去build方法里面去就行了。
但是,看了这篇文章后我才发现还有三种创建SqlSessionFactory对象的实例的方法【震惊】

SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Configuration config)

所以前四种其实是差不多的,而且从build源码里面可以看出这四种方法其实本质上也是构造出Configuration对象的。

在这里再探讨一下environment和properties以及env,和props究竟是何方神圣。
查了很多资料,就是配置文件里面的元素。

因此如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
· 在 properties 元素体内指定的属性首先被读取。
· 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
· 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。
这就是关于配置文件中的properties的解析。
Mybatis配置文件之properties的解析

environment也同理,只要记住最外层函数是优先级最高的就行。这里就不再展开了。
总结:SqlSessionFactory就是ORM经过编译后的一种技术,用工厂设计模式来创建SqlSession。SqlSessionFactory对象的实例是通过SqlSessionFactoryBuilder对象来构建[使用build()方法],具体有两种方法[本质上一样]:1.通过XML配置文件或者一个url。2.传入一个预先定义好的Configuration实例

两三句话花了我一整天去理解,不过也是收获颇多。有什么错误的地方希望大家能够帮我指出来共同进步,希望大家有空可以多多钻研一些概念的事情。不仅有助于了解一些代码底层的事情,而且可以帮助大家在开发的时候少走一些弯路,提高一些效率。

笔记二[2019年09月02日]

已经颓废了够久了,上次写完笔记一之后,突然醍醐灌顶知道了不少知识兴趣暴增。今晚游佳琳一直在微信劝我要迈出第一步去学习。所以今晚第一天来汉堡王学习,感觉效率还不错。以后可能会经常过来。废话不多说,进入正题:

我们来理解一下这段比较绕的话【对于我来说】,第83页,7.2.4章节:

MyBatis在预处理语句(Prepared Statement)中设置一个参数或者从结果集(Resultset)中取出一个值时,都会用其框架内部注册了的typeHandler(类型处理器)进行相关处理。typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType。

还是老样子,从第一个词语入手:预处理语句。
看了一堆资料,终于知道预处理语句是什么东西了[姨母笑,我真的是个憨憨新手]。大概就是MySql的预处理,也有MySqli的。例如你要在程序中会有很多相同的查询语句或者是插入语句一直重复访问数据库,但是预处理就可以先定义一个模板,你只要往里面填参数就行了。防止了Sql注入,而且大大提高效率。我们暂时理解到这里就可以了,不用太深入。
然后我在菜鸟教程那里发现了原来还有MySqli这种东西。是MySql的增强版,每次重复使用只连接同一个进程但是MySql每次都会新建一个进程,PHP5.5已经不支持MySql,支持MySqli(MySql的扩展版)或者是PDO,大家抓紧学起来哦。

继续正文:结果集是什么?ResultSet,大概就是Java执行sql语句后返回的一个ResultSet类型的东西。 ResultSet结果集

        Statement st = conn.createStatement();
        //4 书写sql
        String sql =  "select * from t_user" ;
        //5 执行sql
        ResultSet rs = st.executeQuery(sql); 

TypeHandler看完下面这段应该就能懂:

TypeHandler,类型转换器,在mybatis中用于实现java类型和JDBC类型的相互转换.mybatis使用prepareStatement来进行参数设置的时候,需要通过typeHandler将传入的java参数设置成合适的jdbc类型参数,这个过程实际上是通过调用PrepareStatement不同的set方法实现的;在获取结果返回之后,也需要将返回的结果转换成我们需要的java类型,这时候是通过调用ResultSet对象不同类型的get方法时间的;所以不同类型的typeHandler其实就是调用PrepareStatement和ResultSet的不同方法来进行类型的转换,有些时候会在调用PrepareStatement和ResultSet的相关方法之前,可以对传入的参数进行一定的处理.
当我们没有指定typeHandler的时候mybatis会根据传入参数的类型和返回值的类型调用默认的typeHandler进行处理.对于一个typeHandler需要配置java类型(javaType)和JDBC类型(jdbcType),typeHandler的作用就是实现这两种类型的转换,在传入的参数为指定的Java类型时,将其转换为指定的JDBC类型,当返回值为指定JDBC类型时将其转换为配置的Java类型.
Mybatis之TypeHandler使用教程

大概就是TypeHandler就是MyBatis在预处理的时候用来Java类型和JDBC类型转换的。那么上面还有一个词,框架内部注册的TypeHandler,这个是什么意思:TypeHandlerRegistry()是管理注册TypeHandler的,里面有很多默认常用的类型之间的转换。如果有一些MyBatis默认没有的类型转换,就需要我们自己去编写,并且注册在配置文件里面,注册完之后还是要TypeHandler处理的。上面的链接已经说得很清楚了。所以内部注册的意思就是默认已经帮你注册完的。
最后一个JDBC类型,是什么。其实到现在我还是不知道是什么,对于JDBC的概念只有那个引用的jar包。我们继续查一下资料,先了解JDBC是什么,然后才能知道jdbc的类型是什么【就像了解Java类型一样】:

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API

那API是什么:

应用程序编程接口

那JDBC类型应该和Java类型一样啊,为什么还有TypeHandler转换。奇怪就奇怪在这里了。下次继续更。
(2019年09月03日)查了很多资料,还是找不到为什么JDBC有单独的类型。网上的人说的就是Java类型转成JDBC类型,然后转成数据库类型发送过去。

总结:MyBatis在预先定义好的处理模板中设置一个参数或者从数据库返回的ResultSet类型结果集中取出一个值时,都会用其框架内部注册了的typeHandler(类型处理器)进行相关处理,也可以用自定义注册的typeHandler进行处理(要注意声明typeHandler)。typeHandler的作用就是在预处理语句把传入的参数从Java类型转换为JDBC类型,或者从数据库取出结果时将JDBC类型转换为Java类型。前者实质上是根据实际情况调用PrepareStatement不同的set方法,后者实质上是根据实际情况调用ResultSet对象不同的get方法,用TypeHandlerRegister管理TypeHandler。

笔记二花了大概五六个小时去理解,收获很多,再接再厉。

笔记三[2019年09月03日]

废话不多说,直接进入正题,解析这一大段话,7.2.5,第84页:

MyBatis框架每次创建结果对象的新实例时,都会使用一个对象工厂(ObjectFactory)的实例来完成。MyBatis中默认的ObjectFatory的作用就是实例化目标类,既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。
在通常情况下,我们使用默认的ObjectFactory即可。MyBatis中默认的ObjectFactory是由org.apache.ibatis.reflection.factory.DefaultObjectFactory来提供服务的,大部分场景下都不用配置和修改。如果想覆盖ObjectFactory的默认行为,那么可以通过自定义ObjectFactory实现。

这段话比较长,先看第一段的结果对象,就是结果集对象,简单来说就叫结果集(ResultSet),这个虽然简单,但是很多人看起来一头雾水。

对象工厂,ObjectFactory,从字面意思来说就是工厂模式生产很多对象的地方。【经过了很长时间】查了很多资料,大概意思是ObjectFactory是一个概念性的东西,只是标签的一个名字,实际上常用的是里面的DefaultObjectFactory默认对象工厂类。

在默认的情况下,MyBatis 会使用其定义的对象工厂——DefaultObjectFactory(org.apache.ibatis.reflection.factory.DefaultObjectFactory)来完成对应的工作。
MyBatis 允许注册自定义的 ObjectFactory。如果自定义,则需要实现接口 org.apache.ibatis.reflection.factory.ObjectFactory,并给予配置。
MyBatis ObjectFactory(对象工厂)

参数映射是什么意思?查了一些资料,大概就是从Java传参数给MyBatis到数据库对应的字段参数这个意思。

MyBatis框架每次创建结果对象的新实例时,都会使用一个对象工厂(ObjectFactory)的实例来完成。MyBatis中默认的ObjectFatory的作用就是实例化目标类,既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。

查了我太多资料去了解ObjectFactory的东西(总结包括第二段):

总结:MyBatis根据数据库返回的创建结果集对象的新实例,会使用ObjectFactory去实例化目标类。有两种方式,一种是根据默认的构造方法也就是DefaultObjectFactory;还有一种就是在有参数映射的时候(一般都有),利用有参构造方法去实例化。如果使用默认的一般就不需要修改,但是如果要在实例化的时候实现一些功能,可以继承DefaultObjectFactory写新的类,记得要在配置文件中标明,而且.xml中的objectFactory标签下的property参数可以传到新建的继承类里面的setProperties

笔记四[2019年09月03日]

离下班还有点时间,继续写一下 7.2.7 第84页的内容,摘抄一些比较重要的。

元素是环境配置的根元素,它包含一个default属性,该属性用于指定默认的环境ID。元素的子元素,它可以被定义多个,其id属性用于表示所定义环境的ID值。在元素内,包含事务管理和数据源的配置信息,其中元素用于配置事务管理,它的type属性用于指定事务管理的方式,即使用哪种事务管理器;元素用于配置数据源,它的type属性用于指定使用哪种数据源。

在MyBatis中,可以配置两种类型的事务管理器,分别是JDBC和MANAGED。
JDBC:此配置直接使用JDBC的提交和回滚设置,依赖从数据源得到的连接来管理事务的作用域。
MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。在默认情况下,它会关闭连接,但一些容器并不希望这样,为此可以将closeConnection属性设置为false来阻止它默认的关闭行为。
注意:如果项目中使用Spring+MyBatis,就没有必要在MyBatis中配置事务管理器,因为实际开发中会使用Spring自带的管理器来实现事务管理。

对于数据源的配置,MyBatis框架提供了UNPOOLED、POOLED和JNDI三种数据源类型。
UNPOOLED:配置此数据源类型后,在每次被请求时会打开和关闭连接。它对没有性能要求的简单应用程序是一个很好的选择。
POOLED:此数据源利用“池”的概念将JDBC连接对象组织起来,避免再创建新的连接实例时需要初始化和认证的时间。这种方式使得并发Web应用可以快速地响应请求,是当前流行的处理方式。
JNDI:此数据源可以在EJB或应用服务器等容器中使用。容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。

笔记五[2019年09月05日]

错误更改:第8章第94页 8.1 文件8.1MybatisUtil.java,应该在后面加一个s,变成MybatisUtils.java。应该还有其他地方没改。自己留意一下。
错误更改:第7章第89页 7.3.2中:

在执行上述示例代码时,元素会首先运行,它会通过自定义的语句来设置数据表中的主键(如果t_uesr表中没有记录,就将id设置为1,否则将id的最大值加1作为新的主键),然后调用插入语句。

里面的t_uesr写错了应该是t_user。这本书感觉还有很多错误,但是瑕不掩瑜。

这两天看书加打代码之后MyEclipse里面有个很奇怪的报错:
标红,而且把鼠标放上去显示了The content of element type “dataSource” must match “(property)*”.这个错误。
《Spring+Spring MVC+MyBatis从零开始学》傻瓜式学习笔记_第1张图片
控制台显示了这个错误:

Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.ssm.test.MybatisTest.fingUserByNameAndJobsTest(MybatisTest.java:12)
	at com.ssm.test.Test.main(Test.java:16)
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException: The content of element type "dataSource" must match "(property)*".
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
	at com.ssm.util.MybatisUtil.(MybatisUtil.java:17)
	... 2 more
Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException: The content of element type "dataSource" must match "(property)*".
	at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:259)
	at org.apache.ibatis.parsing.XPathParser.(XPathParser.java:125)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.(XMLConfigBuilder.java:81)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:77)
	... 4 more
Caused by: org.xml.sax.SAXParseException: The content of element type "dataSource" must match "(property)*".
	at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195)
	at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:131)
	at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:384)
	at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:318)
	at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.handleEndElement(XMLDTDValidator.java:2017)
	at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.endElement(XMLDTDValidator.java:901)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1774)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2930)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:807)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
	at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
	at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:225)
	at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:283)
	at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:257)
	... 7 more

网上的都说配置文件[config.xml]没有按照合理的顺序,但是我仔细看了一遍,发现标签的顺序是没有问题的:

properties --> settings --> typeAliases --> typeHandlers --> objectFactory --> objectWrapperFactory --> reflectorFactory --> plugins --> environments --> databaseIdProvider -->mappers
mybatis-config.xml配置文件顺序

我从书里抄的配置文件[mybatis-config.xml]:




	
	
	
		
		
			
			
			
			
				>
				
				>
				
				>
				
				>
				>
			
		
	
	
	
		
	

后来找了很久才发现是这个细节错误:

			
				>
				
				>
				
				>
				
				>
				
			

每一行注释后面打多了一个 “>” 。难怪为什么dataSource那里会标红。我试了一下property右边多加一个 “>”,也发现了一样的错误。
总结:去网上搜索那些错误有时候不可行的话,标签标红的错误可能就是标签包含的东西有误,也就是说本身已经把xml的语法写错了。

笔记六[2019年09月06日]

错误更改:第8章第99页8.3最下面一行:

…同样使用元素对“whee 1=1”条件进行了替换…

里面的whee 1=1改为where 1=1
错误更改:第8章 8.3 第100页第一行第一个括号里面:

…(这里使用where来连接后面的SOL片段)…

我百度了一下,确实没有SOL这个东西。。所以应该改为SQL片段。
片段解析:第8章 8.3 第100页:

元素的作用是取出一些特殊的字符串,它的prefix属性代表的是语句的前缀(这里使用where来连接后面的SQL片段),而prefixOverrides属性代表的是需要去除的那些特殊字符串(这里定义了要去除SQL中的and),上面的写法和使用元素基本事等效的。

这段话意思是解释,但是我还是看不懂,为什么prefix的值要等于where,还有prefixOverrides的值要等于and,去掉and?很奇怪为什么要去掉。查了一些资料:mybatis trim标签的使用 我觉得里面写得挺详细的,还有一些例子帮助理解,下面是常见的属性:

属性 描述
prefix 给sql语句拼接的前缀
suffix 给sql语句拼接的后缀
prefixOverrides 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

大概意思就是说,prefix是表明要在前面添加什么词,例如where,set,update。那么prefixOverrides = and则是避免语句出现类似这种情况:

select * from where and id = 1

前面如果多了一个and就会自动删掉,逗号也同理。suffix和suffixOverrides也一样,只是位置放在后面而已。

你可能感兴趣的:(书籍解析)