Hibernate 拥有 Mybits 的SQL/HQL特性 (注解、XML两不误)

????第一次写博客。文章有点渣,喜欢就看看,不喜欢路过点个赞。

????效果:直接一条语句多种用法

  1. ????FROM User A
  2. ???WHERE
  3. ???1=1
  4. ???<#if id??>
  5. ??????<#if like??>
  6. ?????????and A.id like ‘%‘||:id||‘%‘
  7. ??????<#else>
  8. ?????????and A.id=:id
  9. ??????</#if>
  10. ???</#if>

????先来原理 HQL/SQL + Freemarker 模版生成查询语句。

????1:把SQL/HQL写在XML。

????2:编写文件扫描器(缺)

????3:读取解释XML

????4:按实体类空间缓存查询语句

????5:直接使用

注意:不要直接复制,先弄懂流程,因为这源于旧版本及测试包来写的,也省略了部分代码,因此包路径有问题。程序也不完整,缺了的自己让大家自己去思考实现。基本上依赖Spring,不依赖Spring注入的可以考虑用代理模式注入(反射/生成字节码(javassist/asm)/CGLIB等)

1:为了方便先定义约束Query.dtd

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!ELEMENT QueryList (Alias*,Query*)>
  3. <!--<!ELEMENT Context (CachePool,Bean*,Intercept*,ScanToPack*,CloneModel)>-->
  4. <!ELEMENT Alias EMPTY><!--别名-->
  5. <!ELEMENT Query (#PCDATA)><!--sql/hql-->
  6. ?
  7. <!--QueryList-->
  8. <!ATTLIST QueryList package CDATA #REQUIRED>
  9. <!--Alias-->
  10. <!ATTLIST Alias name CDATA #REQUIRED><!--实体类全名-->
  11. <!ATTLIST Alias Alias CDATA #REQUIRED><!--SQL/HQL 语句中的实体类别名-->
  12. <!--Query-->
  13. <!ATTLIST Query name CDATA #REQUIRED><!--实体类全名-->
  14. <!ATTLIST Query type (HQL|SQL) #REQUIRED><!--语句类型:HQL/SQL-->
  15. <!ATTLIST Query freemarkFormat (false|true) #REQUIRED><!--是否使用FREEMARK标签格式化-->
  16. <!ATTLIST Query resultType CDATA #IMPLIED><!--实体类全名-->
  17. <!ATTLIST Query Alias (true|false) #REQUIRED><!--是否使用了类别名-->

2:建个实体类user.class

  1. package project.master.user;
  2. //import、getting、setting 省略
  3. @Entity
  4. public
    class User extends AbstractEntity {
  5. ???private
    static
    final
    long serialVersionUID = 1L;
  6. [email protected]
  7. ???private String id;
  8. [email protected](unique = true)
  9. ???private String phone;// 用户名(手机号)
  10. ???private String password;
  11. ???private
    int status;// 帐号状态(锁定、停用、正常)
  12. ???private Date lastLogin;
  13. [email protected](updatable = false, nullable = false)
  14. ???private Date createDate = new Date();
  15. }

3:建立对应的XML (User.query.xml)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE QueryList SYSTEM "Query.dtd">
  3. <QueryList package="project.master.user.User">
  4. ???<Alias name="project.master.user.User" Alias="User" />
  5. ???<Alias name="project.freehelp.common.entity.Dictionary" Alias="Dictionary" />
  6. ???<Query name="list" type="HQL" freemarkFormat="true" Alias="true">
  7. ??????<![CDATA[
  8. ?????????FROM User A
  9. ?????????WHERE
  10. ?????????1=1
  11. ?????????<#if id??>
  12. ????????????<#if like??>
  13. ???????????????and A.id like ‘%‘||:id||‘%‘
  14. ????????????<#else>
  15. ???????????????and A.id=:id
  16. ????????????</#if>
  17. ?????????</#if>
  18. ?????????<!-- 各字段判断省略 -->
  19. ??????]]>
  20. ???</Query>
  21. ???<Query name="AAX" type="HQL" freemarkFormat="true" Alias="true">
  22. ??????<!--测试 -->
  23. ??????SELECT A.phone,(SELECT D.value FROM Dictionary D WHERE D.id=‘1‘) as xValue FROM User A
  24. ???</Query>
  25. ???<Query name="checkUser" type="HQL" freemarkFormat="false" Alias="true">
  26. ??????SELECT COUNT(1) FROM User A WHERE A.phone=:phone
  27. ???</Query>
  28. ???<Query name="login" type="HQL" freemarkFormat="false" Alias="true">
  29. ???????FROM User A WHERE A.phone=:phone and A.password=:password
  30. ???</Query>
  31. </QueryList>

4:解析缓存XML

  1. package com.cheuks.bin.db.manager;
  2. //import 省略
  3. public
    class QueryFactory implements QueryType {
  4. ?
  5. ???private
    final Map<String, Template> FORMAT_XQL = new ConcurrentHashMap<String, Template>();
  6. ???private
    final Map<String, String> UNFORMAT_XQL = new ConcurrentHashMap<String, String>();
  7. ???private
    final Configuration freemarkerConfiguration = new Configuration(Configuration.VERSION_2_3_0);
  8. ???private StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
  9. ???private String files;
  10. ???public QueryFactory() {
  11. ??????super();
  12. ??????freemarkerConfiguration.setTemplateLoader(stringTemplateLoader);
  13. ???}
  14. ???public
    synchronized
    void put(String name, String XQL, boolean isFormat) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {
  15. ??????if (null == name || null == XQL)
  16. ?????????return;
  17. ??????if (isFormat) {
  18. ?????????stringTemplateLoader.putTemplate(name, XQL);
  19. ?????????FORMAT_XQL.put(name, freemarkerConfiguration.getTemplate(name));
  20. ??????} else {
  21. ?????????UNFORMAT_XQL.put(name, XQL);
  22. ??????}
  23. ???}
  24. ?
  25. ???public String getXQL(String name, boolean isFormat, Map<String, Object> params) throws TemplateException, IOException {
  26. ??????// if (!isScan)
  27. ??????// scan();
  28. ??????if (!isFormat)
  29. ?????????return UNFORMAT_XQL.get(name);
  30. ??????Template tp = FORMAT_XQL.get(name);
  31. ??????if (null == tp)
  32. ?????????return
    null;
  33. ??????StringWriter sw = new StringWriter();
  34. ??????tp.process(params, sw);
  35. ??????return sw.toString();
  36. ???}
  37. ?
  38. [email protected]("restriction")
  39. [email protected]
  40. ???private
    void scan() {
  41. ??????try {
  42. ?????????Set<String> o = null;
  43. ?????????o = Scan.doScan(files);//扫描所有 *.queue.xml
  44. ?????????xmlExplain(o);
  45. ??????} catch (Exception e) {
  46. ?????????e.printStackTrace();
  47. ??????}
  48. ???}
  49. ?
  50. ???public String getFiles() {return files;}
  51. ???public QueryFactory setFiles(String files) {this.files = files; return
    this;}
  52. ?
  53. ???public
    void xmlExplain(Set<String> urls) throws ParserConfigurationException, SAXException, IOException {
  54. ??????Iterator<String> it = urls.iterator();
  55. ??????SAXParserFactory factory = SAXParserFactory.newInstance();
  56. ??????SAXParser parser = factory.newSAXParser();
  57. ??????xmlHandler handler = new xmlHandler();
  58. ??????XMLReader xmlReader = parser.getXMLReader();
  59. ??????//读取XML
  60. ??????xmlReader.setEntityResolver(new EntityResolver() {
  61. ?????????public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
  62. ????????????return
    new InputSource(this.getClass().getClassLoader().getResourceAsStream("dtd/Query.dtd"));
  63. ?????????}
  64. ??????});
  65. ??????while (it.hasNext()) {
  66. ?????????String str = it.next();
  67. ?????????InputSource is = new InputSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(str));
  68. ?????????is.setEncoding("utf-8");
  69. ?????????xmlReader.setContentHandler(handler);
  70. ?????????xmlReader.parse(is);
  71. ??????}
  72. ???}
  73. ?
  74. ???class xmlHandler extends DefaultHandler {
  75. ??????// private boolean isHQL = false;
  76. ??????private
    boolean format = false;
  77. ??????private
    boolean alias = false;
  78. ??????private String packageName = null;
  79. ??????private String name = null;
  80. ??????Map<String, String> aliases = new HashMap<String, String>();
  81. ??????private String value;
  82. ?
  83. [email protected]
  84. ??????public
    void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  85. ?????????if (qName.equals(QUERY_LIST)) {
  86. ????????????packageName = attributes.getValue(PACKAGE);
  87. ?????????} else
    if (qName.equals(QUERY)) {
  88. ????????????// isHQL = attributes.getValue(TYPE).equals("HQL");
  89. ????????????name = attributes.getValue(NAME);
  90. ????????????format = Boolean.valueOf(attributes.getValue(FREEMARK_FORMAT));
  91. ????????????alias = Boolean.valueOf(attributes.getValue(ALIAS));
  92. ?????????} else
    if (qName.equals(ALIAS)) {
  93. ????????????aliases.put(attributes.getValue(ALIAS), attributes.getValue(NAME));
  94. ?????????}
  95. ?????????super.startElement(uri, localName, qName, attributes);
  96. ??????}
  97. ?
  98. [email protected]
  99. ??????public
    void characters(char[] ch, int start, int length) throws SAXException {
  100. ?????????value = new String(ch, start, length).replaceAll("(\n|\t)", "");
  101. ?????????if (value.length() > 0) {
  102. ????????????try {
  103. ???????????????put(String.format("%s.%s", packageName, name).toLowerCase(), alias ? alias(value) : value, format);//生成缓存
  104. ????????????} catch (Exception e) {
  105. ????????????}
  106. ?????????}
  107. ?
  108. ??????}
  109. ?
  110. ??????private String alias(String str) {
  111. ?????????if (alias)
  112. ????????????for (Entry<String, String> en : aliases.entrySet())
  113. ???????????????str = str.replaceAll(en.getKey(), en.getValue());
  114. ?????????return str;
  115. ??????}
  116. ?
  117. ???}
  118. }

?

5:定义 DBAdapter接口。

  1. ????public
    interface DBAdapter {
  2. ?
  3. ???public DBAdapter setSessionFactory(String name);
  4. ?
  5. ???public <T> List<T> getList(Class<?> c) throws Throwable;
  6. ?
  7. ???/***
  8. ????* query模板查询
  9. ????*/
  10. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, boolean isFormat, Map<String, Object> params) throws Throwable;
  11. ?
  12. ???/***
  13. ????* 模板查询
  14. ????*/
  15. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, Object... params) throws Throwable;
  16. ?
  17. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, int page, int size, Object... params) throws Throwable;
  18. ?
  19. ???/***
  20. ????* query模板查询 * @param queryName 查询语句名
  21. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, boolean isFormat, Map<String, Object> params, int page, int size) throws Throwable;
  22. ?
  23. ???public String queryNameFormat(Class<?> entry, String queryName);
  24. }

6:写实现(AbstractHibernateDBAdapter、HibernateSingleDBAdapter)

AbstractHibernateDBAdapter

  1. package com.cheuks.bin.db.manager;
  2. @SuppressWarnings({ "rawtypes", "unchecked" })
  3. public
    abstract
    class AbstractHibernateDBAdapter implements DBAdapter {
  4. ?
  5. ???private QueryFactory queryFactory;
  6. ?
  7. ???public
    abstract Session getSession();
  8. ?
  9. ???public <T> List<T> getList(Class<?> c) throws Throwable {
  10. ??????return getList(c, -1, -1);
  11. ???}
  12. ?
  13. ???public <T> List<T> getList(Class<?> c, int page, int size) throws Throwable {
  14. ??????Query query = getSession().createQuery(String.format("FROM %s a", c.getSimpleName()));
  15. ??????List list = page > 0 ? page(query, page, size).list() : query.list();
  16. ??????return
    null == list ? null : list;
  17. ???}
  18. ?
  19. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, Object... params) throws Throwable {
  20. ??????return getListByXqlQueryName(queryName, isHQL, -1, -1, params);
  21. ???}
  22. ?
  23. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, int page, int size, Object... params) throws Throwable {
  24. ??????String xql = queryFactory.getXQL(queryName, false, null);
  25. ??????Query query = fillParams(isHQL ? getSession().createQuery(xql) : getSession().createSQLQuery(xql), params);
  26. ??????List list = page > 0 ? page(query, page, size).list() : query.list();
  27. ??????return
    null == list ? null : list;
  28. ???}
  29. ?
  30. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, boolean isFormat, Map<String, Object> params) throws Throwable {
  31. ??????return getListByXqlQueryName(queryName, isHQL, isFormat, params, -1, -1);
  32. ???}
  33. ?
  34. ???public <T> List<T> getListByXqlQueryName(String queryName, boolean isHQL, boolean isFormat, Map<String, Object> params, int page, int size) throws Throwable {
  35. ??????String xql = queryFactory.getXQL(queryName, isFormat, params);
  36. ??????Query query = fillParams(isHQL ? getSession().createQuery(xql) : getSession().createSQLQuery(xql), params);
  37. ??????List list = page > 0 ? page(query, page, size).list() : query.list();
  38. ??????return
    null == list ? null : list;
  39. ???}
  40. ?
  41. ???protected Query fillParams(Query q, Object... o) {
  42. ??????if (null == o || null == q) {
  43. ?????????return q;
  44. ??????}
  45. ??????for (int i = 0, len = o.length; i < len; i++) {
  46. ?????????q.setParameter(i, o[i]);
  47. ??????}
  48. ??????return q;
  49. ???}
  50. ?
  51. ???protected Query fillParams(Query q, Map<String, ?> o) {
  52. ??????if (null == o || null == q) {
  53. ?????????return q;
  54. ??????}
  55. ??????for (Entry<String, ?> en : o.entrySet())
  56. ?????????try {
  57. ????????????q.setParameter(en.getKey(), en.getValue());
  58. ?????????} catch (Exception e) {
  59. ?????????}
  60. ??????return q;
  61. ???}
  62. ?
  63. ???protected Query page(Query q, int pageNum, int size) {
  64. ??????if (pageNum >= 0 && size >= 0) {
  65. ?????????q.setFirstResult(size * (pageNum - 1));
  66. ?????????q.setMaxResults(size);
  67. ??????}
  68. ??????return q;
  69. ???}
  70. ?
  71. ???public String queryNameFormat(Class<?> entry, String queryName) {
  72. ??????return String.format("%s.%s", entry.getName(), queryName).toLowerCase();
  73. ???}
  74. ?
  75. ???public QueryFactory getQueryFactory() {
  76. ??????return queryFactory;
  77. ???}
  78. ?
  79. ???public AbstractHibernateDBAdapter setQueryFactory(QueryFactory queryFactory) {
  80. ??????this.queryFactory = queryFactory;
  81. ??????return
    this;
  82. ???}
  83. }

?

HibernateSingleDBAdapter

  1. package com.cheuks.bin.db.manager;
  2. ?
  3. public
    class HibernateSingleDBAdapter extends AbstractHibernateDBAdapter {
  4. ???//待注入 sessionFactory
  5. ???private SessionFactory sessionFactory;
  6. ???public HibernateSingleDBAdapter setSessionFactory(String name) {
  7. ??????return
    this;
  8. ???}
  9. [email protected]
  10. ???public Session getSession() {
  11. ??????return sessionFactory.getCurrentSession();
  12. ???}
  13. ???public SessionFactory getSessionFactory() {
  14. ??????return sessionFactory;
  15. ???}
  16. ???public HibernateSingleDBAdapter setSessionFactory(SessionFactory sessionFactory) {
  17. ??????this.sessionFactory = sessionFactory;
  18. ??????return
    this;
  19. ???}
  20. }

?

7:注入xml

????<!-- QueryFile 注入 -->

????<bean
id="queryFactory"
class="com.cheuks.bin.db.manager.QueryFactory">

????????<property
name="files"
value="*.query.xml"
/>

????</bean>

????<!--Single DBAdapter 注入 -->

????<bean
id="dBAdapter"
class="com.cheuks.bin.db.manager.HibernateSingleDBAdapter">

????????<property
name="sessionFactory"
ref="sessionFactory"
/>

????????<property
name="queryFactory"
ref="queryFactory"
/>

????</bean>

?

?

8:使用 AbstractDao 、UserDao

AbstractDao

  1. package com.cheuks.bin.db.manager.dao;
  2. public
    abstract
    class AbstractDao<entity, ID extends Serializable> implements BaseDao<entity, ID> {
  3. ?
  4. ???public
    abstract Class<entity> getEntityClass();
  5. ?
  6. ???public
    abstract DBAdapter getDBAdapter();
  7. ?
  8. ???public List<entity> getList(int page, int size) throws Throwable {
  9. ??????return getDBAdapter().getList(getEntityClass(), page, size);
  10. ???}
  11. ?
  12. ???public List<entity> getList(Map<String, Object> params, int page, int size) throws Throwable {
  13. ??????return getDBAdapter().getListByXqlQueryName(getDBAdapter().queryNameFormat(getEntityClass(), "list"), true, true, params, page, size);
  14. ???}
  15. ?
  16. ???public <T> List<T> getList(String queryName, Map<String, Object> params, boolean isFromat, int page, int size) throws Throwable {
  17. ??????return getDBAdapter().getListByXqlQueryName(getDBAdapter().queryNameFormat(getEntityClass(), queryName), true, isFromat, params, page, size);
  18. ???}
  19. ?
  20. ???public <T> List<T> getListCustomQueryName(String queryName, Map<String, Object> params, boolean isFromat, int page, int size) throws Throwable { return getDBAdapter().getListByXqlQueryName(queryName.toLowerCase(), true, isFromat, params, page, size);
  21. ???}
  22. ?
  23. ???public List<entity> getListEntity(String queryName, Map<String, Object> params, boolean isFromat, int page, int size) throws Throwable {
  24. ??????return getDBAdapter().getListByXqlQueryName(getDBAdapter().queryNameFormat(getEntityClass(), queryName), true, isFromat, params, page, size);
  25. ???}
  26. ?
  27. ???public List<entity> getListEntityCustomQueryName(String queryName, Map<String, Object> params, boolean isFromat, int page, int size) throws Throwable {
  28. ??????return getDBAdapter().getListByXqlQueryName(queryName.toLowerCase(), true, isFromat, params, page, size);
  29. ???}
  30. }

UserDao

  1. package project.freehelp.common.dao.impl;
  2. @Component
  3. public
    class UserInfoDaoImpl extends AbstractDao<UserInfo, String> implements UserInfoDao {
  4. [email protected]
  5. ???private DBAdapter dBAdapter;
  6. [email protected]
  7. ???public Class<UserInfo> getEntityClass() {
  8. ??????return UserInfo.class;
  9. ???}
  10. [email protected]
  11. ???public DBAdapter getDBAdapter() {
  12. ??????return dBAdapter;
  13. ???}
  14. }

?

整体就完了。XML部分看DTD。觉得不错可以收藏。但请不要 不名字改了变成自己的成果呀!

时间: 2024-11-10 09:49:53

Hibernate 拥有 Mybits 的SQL/HQL特性 (注解、XML两不误)的相关文章

通过Velocity模板实现了Hibernate sql-query的动态(SQL/HQL)

一.简介 Hibernate对数据库结构提供了较为完整的封装,Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行.而MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架,MyBatis需要使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.在编写比较复杂的动态SQL语句时,Mybatis的SQL是手动编写的,所以

hql 转化为 sql ,满足仅仅用hibernate无法实现的sql构建

如下方法能将hql转换为sql,便于在系统总体是用hibernate查询的情况下,加入一些特殊查询条件的sql语句,满足仅仅用hibernate无法实现的sql构建. protected String hqlToSql(String hql,   org.hibernate.SessionFactory sessionFactory) throws Exception {  org.hibernate.hql.ast.QueryTranslatorImpl queryTranslator = n

hibernate学习系列-----(4)hibernate基本查询上篇:HQL基本查询

紧接着上一篇,今天继续hibernate的学习总结,来聊一聊hibernate的基本查询方法,先说说HQL(hibernate Query Language):它是官方推荐的查询语言.在开始写代码之前,看看需要做哪些准备工作吧,首先,在我们的学生类中新增一个属性"clazz",其实不加也可以,接着,我们需要重写Student.java类中的toString()方法,代码如下: /** * 重写toString方法 */ @Override public String toString(

Hibernate 使用原生 Native SQL

零.  前言 简单的查询一般都使用 HQL , 开发效率会比较高, 但是复杂的查询如果前期用 HQL , 那么后期出现慢查询, 就不得不用 原生 SQL 重写一遍, 要是滥用 HQL 返回各种对象, 用对象到处操作, 最后的慢查询调优简直就是条不归路. 笔者最近在做一个 10 年老项目的慢查询调优, 深有感悟, 本文只介绍 Hibernate 如何使用原生 SQL, 调优工作请看笔者其他文章. 一.  代码示例 使用原生 SQL 的工具类: public SQLQuery findQueryBy

关于hibernate的session.createSQLQuery(sql)直接调用底层SQL后,返回结果集的问题

数据库中有如下信息name,sex张三.男李四.女通过Hibernate的  createSQLQuery 可以直接调用底层SQL语句如下:List list = (List)getHibernateTemplate().execute(new HibernateCallback(){ public Object doInHibernate(Session session) throws HibernateException,SQLException { Query query = sessio

java数据类型,hibernate数据类型,标准sql数据类型之间的对应表

Hibernate API简介 其接口分为以下几类: l         提供访问数据库的操作的接口: l         用于配置Hibernate的接口: l         回调接口 l         扩展Hibernate的功能的接口. 这些接口大多数位于net.sf.hibernate包中 Hibernate的核心接口 5个核心接口: l         Configuration接口:配置Hibernate,根启动Hibernate,创建SessionFactory对象. l   

mybatis简单sql使用java注解而不是xml配置

一直没有系统的接触mybatis,这就导致对其构建模式并没有清晰的认知,所以项目中所有的查询语句都使用在xml中配置,无论是简单sql,还是复杂sql,无一例外的没有使用java注解,这一点,现在看来,真是后悔莫及!那么请你牢记这点原则吧:mybatis简单sql使用java注解而不是xml配置! 再次使用mybatis,觉得有必要重新认识一下它.这就好比,在你上班的路上,如果偶尔抬抬头扫一扫你的周围,也许就会瞥见不一样的风景──非常有气质的美女映入眼帘,你不得不聚精会神的把眼光的焦点全部集中于

030.[转] sql事务特性

sql事务特性简介 pphh发布于2018年10月5日 Sql事务有原子性.一致性.隔离性.持久性四个基本特性,要实现完全的ACID事务,是以牺牲事务的吞吐性能作为代价的.在有些应用场景中,通过分析业务数据读写,使得可以降低事务的隔离性,容忍相应出现的数据一致性问题,实现事务的高并发.高吞吐.低时延性,这是sql事务优化的最佳实践.本文对sql标准中隔离性级别定义和可能会出现的问题进行一一介绍,最后通过Mysql数据库进行相应的演示. 目录 1. Sql事务特性 2. Sql事务特性:原子性 3

SQL 2005 中的XML类型 .

SQL 2005 中的XML类型 . http://blog.csdn.net/sgear/article/details/7349657 SQL Server 2005的XmL数据类型之基础篇一.引言如今,在SQL Server 2005中,XML成为第一流的数据类型.借助于基于XML模式的强类型化支持和基于服务器端的XML数据校验功能,现在,开发者可以对存储的XML文档进行轻松的远程修改.作为数据库开发者,许多人都必须大量地涉及XML. 如今,在SQL Server 2005中,你能以一种新