# XML 语言
xml
与 html
都是标准通用标记语言的子集,SGML (SGM) 标准通用标记语言,是一种定义电子文档结构和描述其内容的国际标准语言,具有极好的扩展性。
html
用于显示数据,其中元素是固定的,浏览器解析执行。xml
用于传输和存储数据,标签可以是用户自定义的,xml
解析器需要自己写。
# 文档声明
xml
文档首先需要使用文档声明来声明文档,且必须出现在文档第一行:
<?xml version="1.0" encoding="GB2312" standalone="yes"?> |
version
是版本号,xml 1.0
版本在 1998 年发布,2004 年发布1.1
版本,但是1.1
版本不向下兼容1.0
版本,所以现在使用的依然是1.0
。encoding
表示编码格式。standalone
表示文档是否独立,即是否依赖其他文档。
没有文档声明的
xml
文档,不是格式良好的xml
文档。文档声明必须从xml
文档的 1 行 1 列开始 也就是必须第一行顶格写。
# XML 元素
元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。
一个标签就是一个元素。
要求:
- 元素都必须有关闭标签,省略关闭标签是非法的。声明不是 XML 的元素,所以第一行的声明,并不需要关闭标签。
- 大小敏感,必须正确嵌套。
- 有且必须只有一个根元素。
标签命名应尽可能简短,可以使用下划线_,避免使用连字符 -。
一个元素(标签)可以有多个属性, xml
属性是键值对形式,如 sex = "male"
,值必须加单(双)引号。
# 注释
CDATA
区段中的文本会被解析器忽略 —— <![CDATA[]]>
。
<script> | |
<![CDATA[to be or not to be]]> | |
</script> |
# 别名
<typeAliases>
标签,这里留着在 Mybatis 讲。
# 解析 XML
JDK 内置了一个
org.w3c
的 XML 解析库。
首先我们创建一个 test.xml
文件,为了简单,直接把它放在项目目录下(与 src
同级)
<?xml version="1.0" encoding="UTF-8" ?> | |
<message> | |
<to>Mike</to> | |
<from>Bob</from> | |
<content>please get up</content> | |
</message> |
这是最基本的 xml
内容,也是 w3c 的例子。
再写一下解析代码:
public static void main(String[] args) { | |
// 创建 DocumentBuilderFactory 对象 | |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
// 创建 DocumentBuilder 对象 | |
try { | |
DocumentBuilder builder = factory.newDocumentBuilder(); | |
// 这里要改成 test.xml 路径,因为直接放在根目录下,所以只需要写文件名 | |
Document d = builder.parse("test.xml"); | |
// 每一个标签都作为一个节点 | |
NodeList nodeList = d.getElementsByTagName("message"); // 可能有很多个名字为 message 的标签 | |
Node rootNode = nodeList.item(0); // 获取首个 | |
// 一个节点下可能会有很多个节点,比如根节点下就囊括了所有的节点 | |
NodeList childNodes = rootNode.getChildNodes(); | |
// 节点可以是一个带有内容的标签(它内部就还有子节点),也可以是一段文本内容 | |
for (int i = 0; i < childNodes.getLength(); i++) { | |
Node child = childNodes.item(i); | |
// 过滤换行符之类的内容,因为它们都被认为是一个文本节点 | |
if(child.getNodeType() == Node.ELEMENT_NODE) | |
System.out.println(child.getNodeName() + ":" +child.getFirstChild().getNodeValue()); | |
// 输出节点名称,也就是标签名称,以及标签内部的文本(内部的内容都是子节点,所以要获取内部的节点) | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} |
结果:
to:Mike | |
from:Bob | |
content:please get up |
Mybatis 也是使用的 JDK 内置的
xml
解析器。
# 初次使用
首先要说明的是,Mybatis 很好用,可以高效代替
JDBC
,但是在某些实际场景中,涉及到复杂的联表查询等操作,还是得乖乖手写 sql 语句,这也警示我们:算法很重要,框架只是节省时间,方便开发。
通过 maven 导入依赖:
<dependency> | |
<groupId>org.mybatis</groupId> | |
<artifactId>mybatis</artifactId> | |
<version>3.4.6</version> | |
</dependency> |
在项目根目录下(web 项目放在 resources 目录)创建 mybatis-config.xml
文件,其实名字随便取:
<?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> | |
<environments default="development"> | |
<environment id="development"> | |
<transactionManager type="JDBC"/> | |
<dataSource type="POOLED"> | |
<property name="driver" value="${驱动类(含包名)}"/> | |
<property name="url" value="${数据库连接URL}"/> | |
<property name="username" value="${用户名}"/> | |
<property name="password" value="${密码}"/> | |
</dataSource> | |
</environment> | |
</environments> | |
</configuration> |
这里以 web 项目为例,将 xml
配置文件放到 Resources 目录里面,构建一个 SqlSessionFactory
:
public class SqlUtil { | |
private static SqlSessionFactory factory; | |
static { | |
try { | |
factory = new SqlSessionFactoryBuilder(). | |
build(Resources.getResourceAsReader("mybatis-config.xml")); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public static SqlSession getSqlSession() { | |
return factory.openSession(true); | |
} | |
} |
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的,我们可以通过 SqlSessionFactory
来创建多个新的会话, SqlSession
对象,每个会话就相当于我不同的地方登陆一个账号去访问数据库,你也可以认为这就是之前 JDBC 中的 Statement
对象,会话之间相互隔离,没有任何关联。
编写一个实体类 Student:
import lombok.Data; | |
// 使用 Lombok 很方便 | |
@Data | |
public class Student { | |
int sid; | |
final String name; | |
public Student(String name) { | |
this.name = name; | |
} | |
} |
根目录下创建一个 mapper 文件夹,新建一个 TestMapper.xml
文件作为映射器:
<?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="TestMapper"> | |
<select id="selectStudent" resultType="com.cyan.entity.Student"> | |
select * from student | |
</select> | |
</mapper> |
需要注意
resultType
属性的包路径,每个人不一样。
其中 namespace
就是命名空间,每个 Mapper 都是唯一的,因此需要用一个命名空间来区分,它还可以用来绑定一个接口。在里面写入了一个 select 标签,表示添加一个 select 操作,同时 id 作为操作的名称,resultType 指定为我们刚刚定义的实体类,表示将数据库结果映射为 Student
类,然后就在标签中写入我们的查询语句即可。
编写好后再配置文件的 <configuration>
标签下,添加这个 Mapper 映射器:
<mappers> | |
<mapper resource="com/cyan/mappers/TestMapper.xml"/> | |
</mappers> |
这里的
TestMapper.xml
放在文件夹内部作为内部资源,使用 resource 指定路径,resource 表示是 Jar 内部的文件。
最后在程序中使用定义好的 Mapper:
public static void main(String[] args) { | |
try(SqlSession sqlSession = SqlUtil.getSqlSession) { | |
List<Student> student = sqlSession.sqlSelectList("selectStudent"); | |
student.forEach(System.out.println); | |
} | |
} |
# 映射接口
直接通过 sqlSession 调用 mapper 有时会不明确,还涉及到类型转换等,所以我们可以将 mapper.xml
与一个接口相关联,在 mappers 文件夹下面创建一个 TestMapper
接口:
public interface TestMapper { | |
List<Student> selectStudent(); | |
} |
接下来:
- 接口与
TestMapper.xml
相关联:TestMapper.xml
的 namespace 改为接口的全类限定名
<mapper namespace="com.cyan.entity.TestMapper"> | |
<select id="selectStudent" resultType="com.cyan.entity.Student"> | |
select * from student | |
</select> | |
</mapper> |
- 通过
SqlSession
获取对应的实现类:
public static void main(String[] args) { | |
try (SqlSession sqlSession = MybatisUtil.getSession(true)){ | |
TestMapper testMapper = sqlSession.getMapper(TestMapper.class); | |
List<Student> student = testMapper.selectStudent(); | |
student.forEach(System.out::println); | |
} | |
} |
TestMapper
接口是 Mybatis 通过动态代理生成的实现类。而不是预先定义好的:
System.out.println( | |
sqlSession.getMapper(TestMapper.class).getClass(); | |
); | |
// 结果:com.sun.proxy.$Proxy4 |
尽管此时
Mybatis
使用起来非常方便,但是配置TestMapper.xml
,映射接口还是有点低效,之后会有更快捷的办法。
# 参考
xml 部分:
- https://www.w3school.com.cn/xml/xml_cdata.asp
- https://www.cnblogs.com/noteless/archive/2018/08/01/9400633.html
Mybatis 部分:
- https://www.yuque.com/qingkongxiaguang/javaweb/gn0syt#af990acc