`
frank-liu
  • 浏览: 1682123 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

mybatis学习总结:基础示例

 
阅读更多

简介

    mybatis是一个比较流行的ORM框架,在很多应用里都有牵涉到。它本身提供对原生sql语句的支持,同时可以让使用者采用更加细粒度的方式来设置对象和数据的映射关系。本文通过一个简单的示例来熟悉mybatis工程的配置和api应用。

 

工程示例

  这个示例采用mysql数据库,结合mybatis的xml配置文件进行设置。

 

创建数据库

  首先,我们创建一个数据库表。详细的创建表的sql脚本如下:

CREATE TABLE user (
  user_id int(10) unsigned NOT NULL AUTO_INCREMENT,
  email_id varchar(45) NOT NULL,
  password varchar(45) NOT NULL,
  first_name varchar(45) NOT NULL,
  last_name varchar(45) DEFAULT NULL,
  PRIMARY KEY (user_id),
  UNIQUE KEY Index_2_email_uniq (email_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  创建好这个表之后,我们将看到如下的表结构信息:

 

 

定义程序依赖

  因为这里要使用mybatis库,这里主要引用了目前最新的3.4.2的版本。另外,程序需要访问mysql数据库,也引用了mysql-connector-java库。因为程序采用maven来管理依赖关系,定义的pom.xml文件内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yunzero</groupId>
  <artifactId>mybatisSample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>mybatisSample</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.4.2</version>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.40</version>
	</dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
	  <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
     <source>1.8</source>
     <target>1.8</target>
     <encoding>${project.build.sourceEncoding}</encoding>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>

 

定义java实体对象

package com.yunzero.mybatisSample.domain;

public class User {
	
	private Integer userId;
	private String emailId;
	private String password;
	private String firstName;
	private String lastName;
	
	@Override
	public String toString() {
		return "User [userId=" + userId + ", emailId=" + emailId
				+ ", password=" + password + ", firstName=" + firstName
				+ ", lastName=" + lastName + "]";
	}
    // getter, setter methods
}

  这里定义了对应的java实体类,也有类似的用户id, emailid等属性。 在定义好数据库表结构以及对应的实体类之后,剩下的就是该考虑怎么配置数据源以及实现它们之间的映射了。

 

定义mapper config文件

  在访问数据库的时候,需要定义数据源相关的信息,创建的相关mapper-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>
	<properties resource="jdbc.properties"/>
	<typeAliases>
		<typeAlias type="com.yunzero.mybatisSample.domain.User" alias="User"/>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="com/yunzero/mybatisSample/mappers/UserMapper.xml"/>
	</mappers>
</configuration>

  我们针对文件里面对应的配置项一一看过来。

properties

properties属性用来定义配置文件里读取属性配置文件的地方。在本示例里引用了jdbc.properties文件。这样在后面的dataSource部分定义数据库相关信息的时候,不用硬编码在xml文件里。而对应的jdbc.properties文件内容如下:

 

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=password

   采用这种配置方式的好处就是将一些牵涉到具体配置以及容易变更的内容放到外部文件,这样在牵涉到部署时可以是的修改更灵活。

 

TypeAliases

  在上面的内容里,我们有一部分这样的内容:<typeAlias type="com.yunzero.mybatisSample.domain.User" alias="User"/>

它主要是给我们定义的实体类建立一个别名。这样,在后面定义具体映射的文件里,可以不用那么繁琐。除了上述的这种配置方式以外,还有一种就是直接引用整个包名的,比如:

 

<typeAliases>
	<package name="com.mybatis3.domain" />
</typeAliases>

  针对这种方式,相当于引入了整个包里所有的实体类,我们可以在映射文件里直接使用定义的类名而不用引用完整的包名。

 

environments

    mybatis里有一个考虑比较全的地方就是引入了environments的配置项,因为在实际项目中,可能会针对不同的环境有不同的配置信息,像开发,测试,集成,产品等环境。所以,我们可以根据需要在这里配置若干个环境的信息。当具体部署到某个环境的时候,直接设置默认的环境信息就可以了。像本示例中,设置的default默认值就是development环境。

  在具体的environment配置项里头,有几个配置项。TransactionManager主要用来设定事物管理的类型,它里面可以设置的值可以是JDBC或者MANAGED两种方式。当设置成JDBC的时候,它表示由应用程序来具体实现管理事物。比如说要在程序逻辑里描述什么时候commit以及什么时候rollback等。

  而如果设置成MANAGED这种方式的话,则表示由具体的应用服务器来负责管理事物。像JBoss, WebLogic, GlassFish等服务器就可以通过EJB来管理应用的事物。

  至于dataSource的配置主要就是设定访问的数据库类型,数据库链接,访问的用户名和密码等信息。dataSource里有一个type属性,它主要有三种类型,分别是UNPOOLED, POOLED和JNDI。其中UNPOOLED是在用户每次发送一个请求的时候创建一个连接,因为连接是临时创建的,所以它并不适合在有一定性能要求的情况下使用。POOLED则表示dataSource创建一个连接池,每次接收到请求的时候就从连接池里取一个连接,用完之后再将连接放回去。而JNDI则通过JNDI接口查询到服务器的配置,由应用服务器上面配置好连接池相关的信息。

 

mapper

    在设定好上述的信息之后,具体描述它们如何映射的地方就是由mapper来指定的。除了上述的采用resource从指定的路径访问文件,还可以从其他的源来引用mapper配置。一些典型的如下:

<mapper url="file:///D:/mybatisdemo/app/mappers/TutorMapper.xml"/>
<mapper class="com.mybatis3.mappers.TutorMapper"/>
<package name="com.mybatis3.mappers"/>

 其中,url可以用来指向一个完整的文件系统或者web url路径。而class用来指向一个mapper接口。package则指向一个包名,在这个包里包含有定义mapper的接口。基于mapper接口的定义方式是和基于xml配置方式不同一种,在后续的文章里会有进一步的讨论。

 

定义mapper接口

  作为完整示例里的一部分,除了定义对象关系的映射,关于对这些数据操作方法的定义也是有必要的。所以,这里就有必要定义数据操作的接口:

 

package com.yunzero.mybatisSample.mappers;

import java.util.List;

import com.yunzero.mybatisSample.domain.User;

public interface UserMapper {
	public void insertUser(User user);
	  
	public User getUserById(Integer userId);
	  
	public List<User> getAllUsers();
	  
	public void updateUser(User user);
	  
	public void deleteUser(Integer userId);
}

  为什么要定义成接口呢?因为在对数据的操作上,我们希望它是独立于具体实现的,所以用接口能够保证。 

 

定义mapper文件

  综合来说,定义好了上述的内容之后,剩下的就是定义详细映射关系的地方了。这里我们采用mapper 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="com.yunzero.mybatisSample.mappers.UserMapper">
 
  <select id="getUserById" parameterType="int" resultType="User">
     SELECT 
      user_id as userId, 
      email_id as emailId , 
      password, 
      first_name as firstName, 
      last_name as lastName
     FROM USER 
     WHERE USER_ID = #{userId}
  </select>
  <!-- Instead of referencing Fully Qualified Class Names we can register Aliases in mybatis-config.xml and use Alias names. -->
   <resultMap type="User" id="UserResult">
    <id property="userId" column="user_id"/>
    <result property="emailId" column="email_id"/>
    <result property="password" column="password"/>
    <result property="firstName" column="first_name"/>
    <result property="lastName" column="last_name"/>   
   </resultMap>
   
  <select id="getAllUsers" resultMap="UserResult">
   SELECT * FROM USER
  </select>
   
  <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId">
   INSERT INTO USER(email_id, password, first_name, last_name)
    VALUES(#{emailId}, #{password}, #{firstName}, #{lastName})
  </insert>
   
  <update id="updateUser" parameterType="User">
    UPDATE USER 
    SET
     PASSWORD= #{password},
     FIRST_NAME = #{firstName},
     LAST_NAME = #{lastName}
    WHERE USER_ID = #{userId}
  </update>
   
  <delete id="deleteUser" parameterType="int">
    DELETE FROM USER WHERE USER_ID = #{userId}
  </delete>
   
</mapper>

  这个文件里定义的很多东西,也值得我们细细看过来。

mapper

    mapper是配置文件里最外层的项,它有一个namespace的属性。这个属性必须和对应的mapper接口一致,这样mybatis就可以找到对应的接口方法来和里面的项映射。

 

resultMap

    resultMap可以说是这个mapper文件里最核心的地方。因为它定义了对象里的元素和表中间具体列元素的对应关系。像它里面的property就是对应类里的成员,column就是表里面的列。有了这个映射关系,在后面的一些CRUD操作的时候,可以引用它作为参数来指导具体的映射。

 

select

  在上面的配置里,我们有两个地方使用了select配置语句。一个是getUserById,一个是getAllUsers。值得注意的是,这里的两个值就是两个select语句里定义的id,同时,它们也是对应mapper接口里对应的方法。它们必须要保持一致。就是因为它们这里的一致才能保证mybatis找到对应的接口方法和具体的配置项关联起来。

  这两个方法里也有不一样的地方,像getUserById里定义了parameterType为int,它对应接口里查询的时候需要提供的int类型的参数。同时,它对应的返回值有resultType=com.yunzero.mybatisSample.domain.User。它设定了返回值的类型。这里值得注意的地方就是,因为它是直接指定返回值是那个类,所以在它的查询语句里

SELECT 
      user_id as userId, 
      email_id as emailId , 
      password, 
      first_name as firstName, 
      last_name as lastName
     FROM USER 
     WHERE USER_ID = #{userId}

  它实际上返回的表里头每个列的字段名必须和定义的User类里的字段一致。否则会导致取不出对应值的问题。

  当然,还有另外一种映射方式,就是在select里面指定resultMap=UserResult,这个时候,我们返回的结果就是期望的了。在实际查询中,我们可以采用设置resultType的方式,也可以采用resultMap的方式。但是不能同时使用两者。

 

insert

  insert方法的描述相对来说并不复杂,它这里有几个地方值得注意。一个是因为它是需要传入一个对应的User对象,所以要指定它的parameterType。另外,由于在前面定义数据库表的时候,对应的user_id项是由数据库生成并自增的。在这种情况下,可以说我们不需要在传入的对象里指定user_id。所以这里需要配置useGeneratedKeys="true" keyProperty="userId" 。其中keyProperty用来设定类里头的哪个成员作为它的key。当然,如果我们插入元素没有设定id自增的话,我们可以在插入元素的语句里把对应的部分加上,也不需要设定useGeneratedKeys这些。

  除了上面这部分。我们也看到insert语句里用到很多#{}包装的参数。它就表示类里头的成员变量。在前面的select语句里也有出现过。只要保证它和类成员的定义是一致的就可以了。

 

update

  关于update的部分和前面insert很类似,也就是设定好对应的方法名,然后对应好参数就可以了。其他就和写sql语句一样。

 

delete

   delete语句的过程也非常类似。一般来说,我们只要保证传入的参数正确就好说了。

  这样,有了上述的这些基本配置,我们mybatis的基本配置工作就弄好了。当然,上面还有很多没有讨论到的地方,比如查询里头如果有更复杂的连接以及级联查询,该怎么实现呢?另外,如果查询等操作需要多个参数,该怎么处理呢?这些内容我们将在后面的文章中进一步讨论。 

 

集成起来

  在配置好上面的内容之后,就该考虑怎么利用mybatis的api来进行具体的数据库操作了。mybatis里头,我们需要首先定义好SqlSessionFactory,通过这个对象再来根据每个具体的请求创建SqlSession。所以我们需要创建一个全局唯一的SqlSessionFactory,因为只要有一个这样的对象就可以创建多个SqlSession了。这部分的代码实现如下:

 

package com.yunzero.mybatisSample.util;

import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisUtil {
	private static SqlSessionFactory factory;

	private MyBatisUtil() { }

	static {
		Reader reader = null;
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage());
		}
		factory = new SqlSessionFactoryBuilder().build(reader);
	}

	public static SqlSessionFactory getSqlSessionFactory() {
		return factory;
	}
}

  详细的实现采用了单例模式,通过读取mybatis-config.xml文件来创建factory对象。 

  创建好factory对象之后,剩下的就是来操作具体的数据库了。我们一般封装建立一个对象来将所有的操作方法放在里面:

package com.yunzero.mybatisSample.service;

import java.util.List;
import org.apache.ibatis.session.SqlSession;

import com.yunzero.mybatisSample.domain.User;
import com.yunzero.mybatisSample.mappers.UserMapper;
import com.yunzero.mybatisSample.util.MyBatisUtil;

public class UserService {

	public void insertUser(User user) {
		SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
		try {
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			userMapper.insertUser(user);
			sqlSession.commit();
		} finally {
			sqlSession.close();
		}
	}

	public User getUserById(Integer userId) {
		SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
		try {
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			return userMapper.getUserById(userId);
		} finally {
			sqlSession.close();
		}
	}

	public List<User> getAllUsers() {
		SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
		try {
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			return userMapper.getAllUsers();
		} finally {
			sqlSession.close();
		}
	}

	public void updateUser(User user) {
		SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
		try {
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			userMapper.updateUser(user);
			sqlSession.commit();
		} finally {
			sqlSession.close();
		}
	}

	public void deleteUser(Integer userId) {
		SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
		try {
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			userMapper.deleteUser(userId);
			sqlSession.commit();
		} finally {
			sqlSession.close();
		}
	}
}

  在上述的代码里,我们可以看到它遵循一个基本的套路,就是首先通过factory的openSession方法来获取SqlSession对象。然后在针对不同操作的时候,通过getMapper引入前面定义的Mapper接口来进行操作。在操作完sqlSession之后,我们还需要关闭它。

  还有一个地方就是,在insertUser, updateUser和deleteUser的操作中,因为要修改数据库,所以采用事物提交的方式防止出现数据的不一致。结合前面配置项里设定的TransactionManager是JDBC,所以这里需要手工的来管理事物的提交和回滚。

  有了上述的基础,我们使用这些类的示例代码如下:

package com.yunzero.mybatisSample;

import java.util.List;

import com.yunzero.mybatisSample.domain.User;
import com.yunzero.mybatisSample.service.UserService;

public class App {
    public static void main(String[] args) {
    	UserService userService = new UserService();
    	User user = new User();
        user.setEmailId("test_email_" + System.currentTimeMillis() + "@gmail.com");
        user.setPassword("secret");
        user.setFirstName("TestFirstName");
        user.setLastName("TestLastName");
        
        // insert user
        userService.insertUser(user);
        System.out.println(user);
        
        // get user by id
        user = userService.getUserById(1);
        System.out.println(user);
        
        
        // list all users
        List<User> list = userService.getAllUsers();
        System.out.println(list.size());
        
        // update user
        user.setLastName("newLastName");
        userService.updateUser(user);
        user = userService.getUserById(1);
        System.out.println(user);
        
        // delete user
        userService.deleteUser(1);
        list = userService.getAllUsers();
        System.out.println(list.size());
    }
}

  这样,一个完整的mybatis的示例就可以运行起来了。代码的详细实现可以看附件里所带的完整示例。 

 

总结

  在这篇文章里,我们主要总结了从头到尾创建一个简单的mybatis示例的过程,并介绍了采用xml配置的方式里的细节。从前面的体验来看,如果只是使用原生的mybatis api来做开发的话,它可以在一定程度上减少代码的冗余,但是总体的工作量还是稍微有点大。在后续的文章里我们会讨论mybatis的其他功能以及采用annotation配置的方式。

 

参考材料

https://www.javacodegeeks.com/2012/11/mybatis-tutorial-crud-operations-and-mapping-relationships-part-1.html 

java persistence with mybatis 3

  • 大小: 25.8 KB
分享到:
评论

相关推荐

    mybatis学习总结:annotation示例改进

    本篇“mybatis学习总结:annotation示例改进”主要关注的是如何利用MyBatis的注解来优化数据库操作。在现代开发环境中,注解已经成为了简化配置、提高代码可读性的重要工具。以下是关于MyBatis注解使用的一些关键...

    mybatis generator生成代码工具的使用, 附demo

    MyBatis Generator(MBG)是一款强大的自动化代码生成工具,它可以极大地提高开发效率,通过配置文件自动生成MyBatis框架所需的Mapper接口、XML映射文件、实体类等代码,从而减少手动编写这些基础代码的工作量。...

    MYBATIS学习资料文件

    最后,“学习笔记”意味着可能有作者对MyBatis学习过程中的理解、经验总结,或者是关键概念的解释,这对于初学者来说是极有价值的参考资料。 【标签解析】 标签“mybatis”进一步确认了这份资源的核心内容,即...

    Mybatis学习文档

    ### Mybatis学习文档知识点概述 #### 一、MyBatis简介与起源 - **起源与发展:** MyBatis起源于Apache的一个开源项目iBatis,最初由Apache Software Foundation托管。2010年,该项目迁移至Google Code并更名为...

    mybatis详细示例操作

    ### MyBatis详细示例操作知识点解析 #### 一、MyBatis简介及特性 ...从下载安装、项目创建到数据源配置、映射文件编写等各个环节进行了详细介绍,帮助初学者快速上手MyBatis框架,并为后续深入学习提供了坚实的基础。

    spring boot+mybatis基础demo

    总结,这个"spring boot+mybatis基础demo"项目旨在提供一个快速入门的平台,让开发者了解如何在Spring Boot应用中集成MyBatis,同时支持不同数据库的切换。通过学习和实践这个示例,可以加深对Spring Boot自动化配置...

    Mybatis学习手册

    ### Mybatis学习手册知识点解析 #### 一、MyBatis简介 - **定义与功能**:MyBatis是一个优秀的持久层框架,支持SQL查询、存储过程及高级映射功能。其核心价值在于几乎消除了所有JDBC代码和参数手工设置以及结果集...

    学习Mybatis,系统带你入门Mybatis

    总结起来,MyBatis作为一款优秀的持久层框架,它通过简化SQL操作、提供动态SQL支持和自动对象映射等功能,大大提升了开发效率,降低了开发难度。在SSM架构中,MyBatis与SpringMVC、Spring协同工作,共同构建了一个...

    mybatis-extension-test.rar

    总结起来,"mybatis-extension-test.rar"提供了一个完整的Mybatis-Extension学习环境,涵盖了从环境配置到实际应用的全过程。通过对解压文件的学习和实践,开发者可以深入理解Mybatis-Extension的使用方式,提升在...

    MyBatis in Practice

    以上是对《MyBatis实战》一书中部分章节内容的总结,涉及MyBatis的基本概念、数据库操作、注解使用、存储过程执行以及与Spring框架的集成等方面的知识点。通过学习这些内容,可以全面掌握MyBatis框架的使用方法和...

    mybatis学习

    ### MyBatis 学习知识点概览 #### 第一章:介绍 - **1.1 整合动机** MyBatis-Spring 的诞生旨在为 Spring ...以上内容总结了 MyBatis 学习的核心知识点,从安装配置到实际应用,为初学者提供了一个全面的学习指南。

    springboot-mybatis.zip

    本教程将详细介绍如何在SpringBoot项目中整合Mybatis,帮助学习者掌握这一核心技术,为后续的Spring整合Shiro奠定基础。 首先,我们来看`springboot.sql`,这是一个数据库初始化脚本。在SpringBoot项目中,通常我们...

    MyBatis 3学习手册

    - **发展演变**:iBatis逐渐演变为MyBatis,并在其基础上进行了大量改进和完善,使其功能更加强大且易于使用。 #### 三、MyBatis的核心组件 - **SqlSessionFactory**:每个MyBatis应用程序的核心是一个...

    spring-boot-learning::rocket:spring-boot学习历程中的实例

    以下是本人学习Spring Boot的历程总结,前面一部分没有写博文很可惜 只有从第10章才开始写 :Spring Boot2(一):使用Spring Boot2集成Mybatis基础搭建 :Spring Boot2(二):使用Spring Boot2集成Mybatis缓存机制 :...

    myBatis.net详细手册

    ### myBatis.net详细手册知识点总结 #### 一、myBatis.net简介 - **定义与定位**:myBatis.net并非纯粹的ORM(Object-Relational Mapping)框架,而是一种半自动化、高度灵活的持久层解决方案。相较于完全自动化的...

    mybatis.docx

    总结,MyBatis作为一款强大的持久层框架,极大地简化了Java与数据库之间的交互,通过学习MyBatis,开发者可以更高效地处理数据库操作,同时提升代码的可读性和可维护性。在实际开发中,结合Spring框架,可以构建出...

    Mybatis入门资源

    "Mybatis总结.pptx"可能是对整个Mybatis学习过程的全面回顾,包括所有上述知识点的综合,也可能包含实际项目中的一些最佳实践。 至于".txt"文件,如"学习(五)用到的表.txt"、"学习(五、六)用到的表.txt"、"快速...

    01MyBatis基础代码.zip

    在"01MyBatis基础代码"这个压缩包中,很可能包含了上述整合步骤的示例代码。通过分析和学习这些代码,可以更直观地理解MyBatis与Spring的整合过程,进一步掌握MyBatis的基本用法,如动态SQL、结果映射、参数映射等。...

Global site tag (gtag.js) - Google Analytics