`
luanmad_java
  • 浏览: 16991 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

Spring+iBatis+JDom递归生成XML树

阅读更多

Spring+iBatis+JDom递归生成XML树

先看看整体结构:
F:\WORKSPACE\TREEXML
│  .classpath
│  .mymetadata
│  .project
│  .treexml.txt
│  luanmad.xml
│ 
├─.myeclipse
├─.settings
│      .jsdtscope
│      org.eclipse.jdt.core.prefs
│      org.eclipse.wst.jsdt.ui.superType.container
│      org.eclipse.wst.jsdt.ui.superType.name
│     
├─ibatisfactory
│      abator.jar
│      abatorConfig.xml
│     
├─src
│  │  applicationContext.xml
│  │  jdbc.properties
│  │  log4j.properties
│  │  sqlmapclient.xml
│  │  treexml.sql
│  │ 
│  └─cn
│      └─luanmad
│          ├─dao
│          │  │  TreeXMLDAO.java
│          │  │ 
│          │  └─impl
│          │          TreeXMLDAOImpl.java
│          │        
│          ├─ibatis
│          │      TreeXMLDO.java
│          │      treexml_SqlMap.xml
│          │     
│          ├─manager
│          │  │  TreeXMLManager.java
│          │  │ 
│          │  └─impl
│          │          TreeXMLManagerImpl.java
│          │        
│          └─treexml
│                  TreeXML.java
│                 
└─WebRoot
    │  index.jsp
    │ 
    ├─META-INF
    │      MANIFEST.MF
    │     
    └─WEB-INF
        │  web.xml
        │ 
        ├─classes
        │  │  applicationContext.xml
        │  │  jdbc.properties
        │  │  log4j.properties
        │  │  sqlmapclient.xml
        │  │  treexml.sql
        │  │ 
        │  └─cn
        │      └─luanmad
        │          ├─dao
        │          │  │  TreeXMLDAO.class
        │          │  │ 
        │          │  └─impl
        │          │          TreeXMLDAOImpl.class
        │          │        
        │          ├─ibatis
        │          │      TreeXMLDO.class
        │          │      treexml_SqlMap.xml
        │          │     
        │          ├─manager
        │          │  │  TreeXMLManager.class
        │          │  │ 
        │          │  └─impl
        │          │          TreeXMLManagerImpl.class
        │          │        
        │          └─treexml
        │                  TreeXML.class
        │                 
        └─lib
                commons-dbcp.jar
                commons-logging.jar
                commons-pool.jar
                ibatis-2.3.0.677.jar
                jdom-1.0.jar
                log4j-1.2.14.jar
                mysql-connector-java-5.1.6-bin.jar
                spring.jar


1.TreeXMLDAOImpl.java,DAO层的实现


代码:
package cn.luanmad.dao.impl;

import java.util.List;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import cn.luanmad.dao.TreeXMLDAO;

/**
* 类说明
*
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午07:02:35
*/
public class TreeXMLDAOImpl extends SqlMapClientDaoSupport implements TreeXMLDAO
{

public List queryAllForList(String sql)
{
return this.getSqlMapClientTemplate().queryForList(sql);
}

}
2.TreeXMLDAO.java,DAO层的接口


代码:
package cn.luanmad.dao;

import java.util.List;

/**
* 类说明
*
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午07:00:34
*/
public interface TreeXMLDAO
{
List queryAllForList(String sql);
}
3.实体TreeXMLDO.java,对就数据库treexml


代码:
package cn.luanmad.ibatis;

/**
*
* 类说明
*
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午06:52:27
*
*/
public class TreeXMLDO
{
private Integer id;

private String name;

private Integer pid;

public Integer getId()
{
return id;
}

public void setId(Integer id)
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public Integer getPid()
{
return pid;
}

public void setPid(Integer pid)
{
this.pid = pid;
}
}
4.treexml_SqlMap.xml ,iBatis生成的SqlMap
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="treexml" >
<typeAlias alias="treeXMLDO" type="cn.luanmad.ibatis.TreeXMLDO"/>
<resultMap id="treeXMLDOMap" class="treeXMLDO" >
<result column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="pid" property="pid" jdbcType="INTEGER" />
</resultMap>
<select id="treeXMLDO.selectAll" resultMap="treeXMLDOMap" parameterClass="treeXMLDO" >
select id, name, pid
from treexml
</select>
</sqlMap>

5.TreeXMLManagerImpl .java,Manager层的实现


代码:
package cn.luanmad.manager.impl;

import java.util.List;

import cn.luanmad.dao.TreeXMLDAO;
import cn.luanmad.manager.TreeXMLManager;

/**
* 类说明
*
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午07:19:59
*/
public class TreeXMLManagerImpl implements TreeXMLManager
{
private TreeXMLDAO treeXMLDAO;

public TreeXMLDAO getTreeXMLDAO()
{
return treeXMLDAO;
}

public void setTreeXMLDAO(TreeXMLDAO treeXMLDAO)
{
this.treeXMLDAO = treeXMLDAO;
}

public List queryAllForList(String sql) throws Exception
{
return treeXMLDAO.queryAllForList(sql);
}

}
6.TreeXMLManager .java ,Manager层的接口


代码:
package cn.luanmad.manager;

import java.util.List;

/**
* 类说明
*
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午07:19:32
*/
public interface TreeXMLManager
{
List queryAllForList(String sql)throws Exception;
}
7.TreeXML.java,JDOM递归生成XML树


代码:
package cn.luanmad.treexml;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.luanmad.ibatis.TreeXMLDO;
import cn.luanmad.manager.TreeXMLManager;
import cn.luanmad.manager.impl.TreeXMLManagerImpl;

/**
* 类说明
* JDom递归生成XML树
* @author luanmad!
* @version V1.0 创建时间:2009-8-14 下午23:51:56
*/
public class TreeXML
{


// 创建根节点 TREENODES;
Element root = new Element("LuanmadNodes");

// 根节点添加到文档中;
Document doc = new Document(root);

Element elements = null;

@SuppressWarnings("unchecked")
private boolean hasChild(List list, Integer pid)
{
Iterator iterator = list.iterator();
boolean bool = false;
while (iterator.hasNext())
{
TreeXMLDO t = (TreeXMLDO) iterator.next();
if (pid.equals(t.getPid()))
{
bool = true;
break;
}
}
return bool;
}

private void createFile(Integer pid, Element p, List list)
{
if (p == null)
p = root;
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < list.size(); i++)
{
TreeXMLDO temp = (TreeXMLDO) list.get(i);

// 创建当前节点对象
Element nowNode = new Element("luanmadNode");

/* 存入ID时的规则(1.只用当前ID;2.用父ID+当前ID) */
stringBuffer.delete(0, stringBuffer.length());
stringBuffer.append(temp.getId());

nowNode.setAttribute("id", stringBuffer.toString());
nowNode.setAttribute("pid", temp.getPid().toString());
nowNode.setAttribute("name", temp.getName());

// 找到指定级别的子类
if (pid.equals(temp.getPid()))
{
// 这里是打印,可以换成输出,或是做别的处理
System.out.println("id="+temp.getId()+ ",pid=" + pid+",name="+ temp.getName() );
p.addContent(nowNode); // 加入当前节点到父节点

// 判断是否存在下级节点,若存在读取所有节点
if (hasChild(list, temp.getId()))
createFile(temp.getId(), nowNode, list);
}

}
saveXmlFile();
}

/*把数据写入文件*/
private void saveXmlFile()
{
XMLOutputter xmlout = new XMLOutputter();
String path = "luanmad.xml";
try
{

xmlout.output(doc, new FileOutputStream(path));
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}

/* 创建树的入口 */
private void start(List list)
{
Element e = null;
createFile(0, e, list);
}

@SuppressWarnings("unchecked")
/*Spring和iBatis整合从数据库里获得数据*/
public List getTreeXMLList()
{
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
TreeXMLManager treeXMLManager = (TreeXMLManagerImpl) context
.getBean("treeXMLManager");

List list = new ArrayList();
try
{

list = (List) treeXMLManager
.queryAllForList("treeXMLDO.selectAll");
Iterator iterator = list.iterator();
System.out.println("----------------原来顺序-----------------");
while (iterator.hasNext())
{
TreeXMLDO treeXMLDO = (TreeXMLDO) iterator.next();
System.out.println("id=" + treeXMLDO.getId() + ",pid="
+ treeXMLDO.getPid() + ",name=" + treeXMLDO.getName());
}
System.out.println("----------------构树后顺序-----------------");
}
catch (Exception e)
{
e.printStackTrace();
}
return list;
}

@SuppressWarnings("unchecked")
public static void main(String[] argStrings)
{

TreeXML treeXML= new TreeXML();

treeXML.start(treeXML.getTreeXMLList());

}


}
8.applicationContext.xml,spring配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd ">

<!-- Spring高级配置 可配置的数据库链接 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>

<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="${jdbc.driver}">
</property>
<property name="url"
value="${jdbc.url}">
</property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="configLocation">
<value>classpath:sqlmapclient.xml</value>
</property>
</bean>
<!-- 共用Dao -->
<bean id="treeXMLDAO" class="cn.luanmad.dao.impl.TreeXMLDAOImpl">
<property name="dataSource" ref="dataSource"></property>
<property name="sqlMapClient" ref="sessionFactory"></property>
</bean>

<bean id="treeXMLManager" class="cn.luanmad.manager.impl.TreeXMLManagerImpl">
<property name="treeXMLDAO" ref="treeXMLDAO"></property>
</bean>
<!-- 配置多个applicationContext
<import resource="applicationContext-news.xml"/>
<import resource="applicationContext-ibatisTest.xml"/>-->



<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 共用事务 -->
<!--声明Spring事务的传播行为和隔离级别 -->
<tx:advice id="methodAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="select*" read-only="true"/>
<tx:method name="update*" propagation="REQUIRED"/><!-- 防止脏读、不可重复读 -->
<tx:method name="delete*"/>
<tx:method name="insert*"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!--
<aop:config>
<aop:pointcut id="classpointcut" expression="execution(* manager.*.*(..))"/>
<aop:advisor advice-ref="methodAdvice" pointcut-ref="classpointcut"/>
</aop:config>
-->

</beans>

9.数据库jdbc.properties配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:2008/luanmad
jdbc.user=root
jdbc.password=admin

10.日志配置

log4j.rootLogger=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n

log4j.logger.java.sql.PreparedStatement=DEBUG

11.sqlmapclient.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<!--
Settings各节点的说明:
cacheModelsEnable :是否启用SqlMapClient上的缓存机制,建议设为true;
enhancementEnabled:是否针对POJO启用字节码增加机制以提升getter/setter的调用效能,避免使用Java reflect所带来的性能开销,
同时也为Lazy Loading带来了极大的性能提升,建议设置为true;
lazyLoadingEnabled:是否启用延迟加载机制,建议设为"true";
errorTracingEnabled:是否启用错误日志,在开发期间建议设为"true" 以方便调试;
maxRequests:最大并发请求数(Statement并发数);
maxSessions:最大Session 数。即当前最大允许的并发SqlMapClient数。maxSessions设定必须介于maxTransactions和maxRequests之间,
即maxTransactions<maxSessions=<maxRequests;
maxTransactions:最大并发事务数;
useStatementNamespaces:是否使用Statement命名空间。这里的命名空间指的是映射文件中,sqlMap节点的namespace属性,如针对users表的映射文件sqlMap节点:
<sqlMap namespace="Users">这里,指定了此sqlMap节点下定义的操作均从属于"Users"命名空间。在 useStatementNamespaces="true"的情况
下,Statement调用需追加命名空间,如:sqlMap.update("Users.updateUser",user);否则直接通过Statement名称调用即可,如:
sqlMap.update("updateUser",user);但请注意此时需要保证所有映射文件中,Statement定义无重名。
-->

<sqlMapConfig>
<settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
errorTracingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5"
useStatementNamespaces="false"
/>
<!--
transactionManager节点定义了iBatis的事务管理器,提供三种方式,(1、JDBC,2、jta:分布式数据库,3、EXTERNAL:itbatis本身不做事务处理由外部进行处理);
dataSource节点:从属于transactionManager节点,用于设定ibatis运行期使用的DataSource属性;
type属性:type属性指定了dataSource的实现模式,共三种模式,(1、simple:ibatis提供的较小的连接池 2、dbcp:是apache实现的连接池
3、jndi:tomcate或weblogic提供的服务);
JDBC.Driver:JDBC驱动;
JDBC.ConnectionURL:数据库连接URL,如果用的是SQLServer JDBC Driver,需要在url后追加SelectMethod=Cursor以获得JDBC事务的多Statement支持;
JDBC.Username:数据库用户名;
JDBC.Password:数据库用户密码;
Pool.MaximumActiveConnections:数据库连接池可维持的最大容量;
Pool.MaximumIdleConnections:数据库连接池中允许的挂起(idle)连接数;
Pool.MaximumCheckoutTime数据库连接池中,连接被某个任务所允许占用的最大时间,如果超过这个时间限定,连接将被强制收回,(毫秒);
Pool.TimeToWait:当线程试图从连接池中获取连接时,连接池中无可用连接可供使用,此时线程将进入等待状态,直到池中出现空闲连接。此参数设定了线程所允许等待的最长时间,(毫秒);
Pool.PingQuery:数据库连接状态检测语句。某些数据库在某段时间持续处于空闲状态时会将其断开。而连接池管理器将通过此语句检测池中连接是否可用,
检测语句应该是一个最简化的无逻辑SQL。如“select 1 from user”,如果执行此语句成功,连接池管理器将认为此连接处于可用状态;
Pool.PingEnabled:是否允许检测连接状态;
Pool.PingConnectionsOlderThan:对持续连接时间超过设定值(毫秒)的连接进行检测;
Pool.PingConnectionsNotUsedFor:对空闲超过设定值(毫秒)的连接进行检测;
-->
<!--<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property name="JDBC.Driver"
value="com.microsoft.jdbc.sqlserver.SQLServerDriver" />
<property name="JDBC.ConnectionURL"
value="jdbc:microsoft:sqlserver://localhost:1433;databaseName=pubs;selectMethod=cursor" />
<property name="JDBC.Username" value="sa" />
<property name="JDBC.Password" value="" />
<property name="Pool.MaximumActiveConnections" value="10" />
<property name="Pool.MaximumIdleConnections" value="5" />
<property name="Pool.MaximumCheckoutTime" value="120000" />
<property name="Pool.TimeToWait" value="500" />
<property name="Pool.PingQuery"
value="select 1 from ACCOUNT" />
<property name="Pool.PingEnabled" value="false" />
<property name="Pool.PingConnectionsOlderThan" value="1" />
<property name="Pool.PingConnectionsNotUsedFor" value="1" />
</dataSource>
</transactionManager>-->
<!-- sqlMap 节点指定了映射文件的位置,配置中可出现多个sqlMap 节点,以指定项目内所包含的所有映射文件 -->
<sqlMap resource="cn/luanmad/ibatis/treexml_SqlMap.xml" />
</sqlMapConfig>

11.treexml.sql,创建数据
/*
SQLyog 浼佷笟鐗� - MySQL GUI v7.14
MySQL - 6.0.5-alpha-community : Database - luanmad
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

CREATE DATABASE /*!32312 IF NOT EXISTS*/`luanmad` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `luanmad`;

/*Table structure for table `treexml` */

DROP TABLE IF EXISTS `treexml`;

CREATE TABLE `treexml` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`pid` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

/*Data for the table `treexml` */

insert into `treexml`(`id`,`name`,`pid`) values (1,'aaa',0),(2,'bbb',0),(3,'ccc',1),(4,'ddd',3),(5,'eee',3),(6,'fff',2),(7,'ggg',4),(8,'hhh',6);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

12.用ibator.jar自动生成SQLMAP的配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE abatorConfiguration PUBLIC "-//Apache Software Foundation//DTD Abator for iBATIS Configuration 1.0//EN"
"http://ibatis.apache.org/dtd/abator-config_1_0.dtd">

<abatorConfiguration>
<abatorContext><!-- TODO: Add Database Connection Information -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
c userId="root"
password="admin">

<classPathEntry
location="..\WebRoot\WEB-INF\lib\mysql-connector-java-5.1.6-bin.jar" />
</jdbcConnection>
<javaModelGenerator targetPackage="cn.luanmad.ibatis"
targetProject="..\src" />
<sqlMapGenerator targetPackage="cn.luanmad.ibatis" targetProject="..\src" />
<daoGenerator type="IBATIS" targetPackage="dao"
targetProject="..\src" />


<table schema="luanmad" tableName="treexml" domainObjectName="TreeXMLDO">
</table>
</abatorContext>
</abatorConfiguration>

本人用的是myeclipse7.1没有找到好的ibatis生成sqlmap的插件。本例是用ibator.jar自动手动生成的sqlmap。在DOS下先切换到ibator.jar的目录下(如F:\workspace\TreeXML\ibatisfactory),然后输入命令
F:\workspace\TreeXML\ibatisfactory>java -jar abator.jar abatorConfig.xml true
就会自动生成cn\luanmad\ibatis目录,并生成treexml_SqlMap.xml和TreeXMLDO(只留此两文件就行,其余可以去掉)
还会生成cn\luanmad\dao目录,并生成TreeXMLDAO.java文件和TreeXMLDAOImpl.java,自已动手建个 cn\luanmad\dao\impl目录,将TreeXMLDAOImpl.java文件放入。到此基本上可以了。赶快试试吧!

  • 大小: 7.9 KB
  • 大小: 25 KB
分享到:
评论

相关推荐

    spring+ibatis+jDom递归生成

    当我们谈论“spring+ibatis+jDom递归生成”时,可能是指在Spring应用中,使用iBatis进行数据查询,然后利用jDom解析返回的XML数据,通过递归方法构建复杂的对象树。以下是一种可能的实现方式: 1. 首先,在Spring...

    最新Java面试宝典pdf版

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你...

    Java面试宝典-经典

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你...

    java面试题大全(2012版)

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你...

    Java面试宝典2012版

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、...

    java面试宝典2012

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 124 4、编程用JAVA解析XML的方式. 125 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 127 七. 流行的框架与新技术 128 1、谈谈你...

    Java面试宝典2012新版

    3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你...

Global site tag (gtag.js) - Google Analytics