mybatis自己学习的一些总结

以前一直在使用spring的JDBCTEMPLATE和hibernate做项目;两个都还不错,spring的jdbctemplate用起来比较麻烦,虽然很简单。而hibernate呢,用起来很好用,很方便,但是很多规矩,规则还有方法到现在都还是入门阶段。所以我就学习了一下mybatis来充实一下自己。

mybatis的学习(我也只是入门),我参考了这个博客地址----大神的总结,通过对他的博客的一些学习,我做了一些自己的总结,所以下面的一些内容都是自己在学习的时候的一些东西,也是参考着大神的博客来做的。当然做了一些错误的修改和自己理解的实现。

mybatis实战教程(mybatis in action)之一:开发环境搭建

1.关于数据库表的创建,参见大神的博文,这里就不再多说;

2.在user_src下的Configuration.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>
	<!--这里给我们要使用的数据实体类设置一个在mybatis中使用的别名,以后在mybatis中直接使用他们的别名来映射到对应的实体对象中 -->
	<typeAliases>
		<typeAlias alias="User" type="com.mpc.mybaits.model.User" />
		<typeAlias alias="Article" type="com.mpc.mybaits.model.Article" />
	</typeAliases>

	<!-- 指定mybatis中使用的数据库环境,默认使用development,其中development使用的是JDBC来链接数据库,数据库是mysql -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybaits" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>

	<!--这里使用mapper来指定相应的实体类的处理方法,(这些mapper就是处理数据的增删改查方法的xml集合,在这里面你可以写任何的查询方法 
		,在这些xml中我们可以使用typeAliases中申明的对象来包装我们的查询结果,传入查询参数), 这写mapper都是可以写任意的查询方法的,(任意的表,任意的查询语句)但是在开发中我们为每一个类指定一个mapper文件,这样是为了方便管理和项目的开发 
		;总之就是一点:mapper和typeAliases是没有任何对应关系的,只是为了开发方便才会有什么Usermapper、Articlemapper这些分类 -->
	<mappers>
		<mapper resource="com/mpc/mybaits/model/User.xml" />
	</mappers>
</configuration>

3.具体User类的实现参照大神的博文,我这里就不献丑了。只说一句:这个类是用在typeAliases那里的;

4.和User类在同一个包下的User.xml,这个User.xml是用在mappers中的,它的作用是提供数据库增删改查的方法。

<?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">
<!-- 这里我们要通过xml来进行数据库的操作,所以我饿美女给这个mapper指定一个唯一的空间名称, -->
<mapper namespace="com.mpc.mybaits.model.UserMapper">

	<!-- 这里定一个select方法,从user表中查找指定id的数据,然后用 resultType="User" User类来进行包装 -->
	<!--  id是这个方法的唯一标识,通过id来调用这个方法,parameterType是传入的参数的类型,这里是整形。通过#{xxx}来获得传入的参数-->
	<select id="selectUserByID" parameterType="int" resultType="User">
		select * from `user` where id = #{id}
	</select>

</mapper>
5.建立测试类

package com.mpc.test;

import java.io.Reader;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Assert;
import org.junit.Test;

import com.mpc.mybaits.model.User;

public class Test1 {

	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {// 在一个static块中处理我们的相关初始化操作,这里做的操作是用Configuration.xml中的配置信息来初始化一个mybatis使用的sqlSessionFactory
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Test
	public void test() {
		SqlSession session = sqlSessionFactory.openSession();// 获得session
		Assert.assertNotNull(session);// 断言session不为空
		try {
			// 通过session来调用com.mpc.mybaits.model.UserMapper.selectUserByID方法,传入的参数是1
			// 其中com.mpc.mybaits.model.UserMapper是我们的mapper的命名空间。selectUserByID是方法的id
			User user = (User) session.selectOne(
					"com.mpc.mybaits.model.UserMapper.selectUserByID", 1);
			Assert.assertNotNull(user);// 断言user不为空
			Assert.assertEquals(
					"User(id=1, userName=summer, userAge=100, userAddress=shanghai,pudong)",
					user.toString());// 对user内容的断言
		} finally {
			session.close();// 最后要关闭session
		}

	}

	// public static void main(String[] args) {
	// SqlSession session = sqlSessionFactory.openSession();
	// try {
	// User user = (User) session.selectOne(
	// "com.mpc.mybaits.model.UserMapper.selectUserByID", 1);
	// System.out.println(user.toString());
	// } finally {
	// session.close();
	// }
	// }
}

测试结果:





mybatis实战教程(mybatis in action)之二:以接口的方式编程

使用接口编程的方式可以提高我们在方法中使用mybatis的效率,减少很多不必要错误,让我们不用总是查来查去,思来想去。。

1.在inter包中定义我们的接口

package com.mpc.mybaits.inter;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;

import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;
import com.mpc.mybaits.utils.PageInfo;

/**
 * @author Administrator
 *
 */
public interface IUserOperation {

	/**
	 * 方法的名称必须与User。xml中的 select 的id 对应(<select id="selectUserByID")
	 * 
	 * @param id
	 * @return
	 */
	public User selectUserByID(int id);

	
}
2.修改user.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="ccom.mpc.mybaits.inter.IUserOperation">

	<!-- 这里定一个select方法,从user表中查找指定id的数据,然后用 resultType="User" User类来进行包装 -->
	<!--  id是这个方法的唯一标识,通过id来调用这个方法,parameterType是传入的参数的类型,这里是整形。通过#{xxx}来获得传入的参数-->
	<select id="selectUserByID" parameterType="int" resultType="User">
		select * from `user` where id = #{id}
	</select>

</mapper>
3.测试方法

package com.mpc.test;

import java.io.Reader;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.User;

public class Test2 {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Test
	public void test() {
		new Test1();
		SqlSession session = sqlSessionFactory.openSession();

		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);//通过session来获得IUserOperation的实例对象
			User user = iUserOperation.selectUserByID(1);//直接调用接口中 的方法就可以了
			System.out.println(user.toString());
		} finally {
			session.close();
		}
	}
}

测试结果:



mybatis实战教程(mybatis in action)之三:实现数据的增删改查

*用list来返回多个查询结果,要使用list来返回多个查询结果,那么我们要定义自己的resultMap;

在User.xml中加入自己定义的resultMap;

	<!-- 为了返回list 类型而定义的returnMap -->
	<resultMap type="User" id="resultListUser">
		<id column="id" property="id" />
		<result column="userName" property="userName" />
		<result column="userAge" property="userAge" />
		<result column="userAddress" property="userAddress" />
	</resultMap>


这是大神的博文中提到的,但是根据我自己的实践···这里完全没有必要使用resultMap,使用resultType就可以达到目的,所以我在user.xml中使用如下语句也是可以的:

	<select id="selectUsers" parameterType="string" resultType="User">
		select * from user where userName like #{userName}
	</select>

当然按照大神的博文中记载的方式也是可以的--》在User.xml中加入新的查询语句;

	<!-- 返回list 的select 语句,注意 resultMap 的值是指向前面定义好的 -->
	<select id="selectUsers" parameterType="string" resultMap="resultListUser">
		select * from user where userName like #{userName}
	</select>


个人理解,resultMap就是我们自己定义的一种返回结果的包装,在从表中查处数据口,MyBatis把所有的查询结果都放在一个map中,都是用key-value的形式存放的,如果指定的是resultType,那么Mybatis会为我们进行封装,指定的为resultMap的话,Mabatis会按照resultMap定义的格式来进行封装而已-----------;


在IUserOperation接口中加入resulListUser这个id对应的接口方法----
public List<User> selectUsers(String userName);


测试方法:

package com.mpc.test;

import java.io.Reader;
import java.util.List;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.User;

public class Test2 {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Test
	public void test() {
		new Test1();
		SqlSession session = sqlSessionFactory.openSession();

		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);// 通过session来获得IUserOperation的实例对象
			List<User> list = iUserOperation.selectUsers("%");
			for (int i = 0; i < list.size(); i++) {
				System.out.println(list.get(i).toString());
			}
		} finally {
			session.close();
		}
	}
}


测试结果:



*使用mybatis来增加数据

在User.xml中添加如下代码:

	<!--执行增加操作的SQL语句。id和parameterType 分别与IUserOperation接口中的addUser方法的名字和 参数类型一致。以#{name}的形式引用Student参数 
		的name属性,MyBatis将使用反射读取Student参数 的此属性。#{name}中name大小写敏感。引用其他 的gender等属性与此一致。seGeneratedKeys设置 
		为"true"表明要MyBatis获取由数据库自动生成的主 键;keyProperty="id"指定把获取到的主键值注入 到Student的id属性 -->
	<insert id="addUser" parameterType="User" useGeneratedKeys="true"
		keyProperty="id">
		insert into user(userName,userAge,userAddress)
		values(#{userName},#{userAge},#{userAddress})
	</insert>

在IUserOperation接口中加入addUser这个id所对应的接口方法public void addUser(User user);

Mybatis在接受到user对象以后,会把user的属性都封装到一个map中,key是user的属性名,value就是user的属性值了。所在通过#{属性名}在user.xml中就能取得传入的user的属性值。

测试方法:

package com.mpc.test;

import java.io.Reader;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.User;

public class Test3 {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		User user = new User();
		user.setUserAddress("人民广场123");
		user.setUserName("飞鸟123");
		user.setUserAge("80123");
		SqlSession session = sqlSessionFactory.openSession();
		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);
			iUserOperation.addUser(user);
			session.commit();// 必须的,不然的话是保存不到数据库中的
			System.out.println("添加了用户,当前用户的id为" + user.getId());
		} finally {
			session.close();
		}
	}
}

测试结果:





*使用mybatis来更新数据

在User.xml中添加如下代码

	<update id="updateUser" parameterType="User">
		update user set
		userName=#{userName},userAge=#{userAge},userAddress=#{userAddress}
		where id=#{id}
	</update>

在IUserOperation中添加对应的接口方法

public void updateUser(User user);


测试方法

package com.mpc.test;

import java.io.Reader;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.User;

public class Test4 {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		SqlSession session = sqlSessionFactory.openSession();
		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);
			User user = iUserOperation.selectUserByID(6);
			user.setUserAddress("太平天国");
			iUserOperation.updateUser(user);
			session.commit();// 必须的,不然的话是保存不到数据库中的
			System.out.println("更新了用户,当前用户的id为" + user.getId());
		} finally {
			session.close();
		}
	}
}


测试结果





*mybatis删除数据

在User.xml中添加如下代码

	<delete id="deleteUser" parameterType="int">
		delete from user where
		id=#{id}
	</delete>
在IUserOperaiton中添加对应的接口方法:

	public void deleteUser(int id);

测试方法:

package com.mpc.test;

import java.io.Reader;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.User;

public class TestDelete {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		SqlSession session = sqlSessionFactory.openSession();
		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);
			User user = iUserOperation.selectUserByID(4);
			iUserOperation.deleteUser(6);
			session.commit();// 必须的,不然的话是保存不到数据库中的
			System.out.println("删除了用户,当前用户的id为" + user.getId());
		} finally {
			session.close();
		}
	}
}


mybatis实战教程(mybatis in action)之四:实现关联数据的查询

1.article表和实体的创建参加大神的博文,这里我就不写了。

2.在User.xml中添加如下代码

	<!-- User 联合文章进行查询 方法之一的配置 (多对一的方式) -->
	<resultMap id="resultUserArticleList" type="Article">
		<id property="id" column="aid" />
		<result property="title" column="title" />
		<result property="content" column="content" />

		<association property="user" javaType="User">
			<id property="id" column="id" />
			<result property="userName" column="userName" />
			<result property="userAddress" column="userAddress" />
			<result property="userAge" column="userAge" />
		</association>
		<!-- <association property="user" javaType="User" resultMap="resultListUser" 
			/> -->
	</resultMap>
这里要使用到resultMap,因为我们的数据库中的表结构和我们自己定义的类的字段是不对应的,通过联合查询以后,mybatis并不知道要用怎样的映射关系来专配,所以我们在这里自己定义。

在User.xml中加入查询代码

	<select id="getUserArticles" parameterType="int"
		resultMap="resultUserArticleList">
		select user.id,user.userName,user.userAddress,user.userAge,article.id
		aid,article.title,article.content from user,article
		where
		user.id=article.userid and user.id=#{id}
	</select>
3.在IUserOperation中加入相应的接口方法

public List<Article> getUserArticles(int userid);
4.测试方法

package com.mpc.test;

import java.io.Reader;
import java.util.List;

import lombok.Getter;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;

public class Test5 {
	private static @Getter SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("Configuration.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		SqlSession session = sqlSessionFactory.openSession();
		try {
			IUserOperation iUserOperation = session
					.getMapper(IUserOperation.class);
			List<Article> articles = iUserOperation.getUserArticles(1);
			System.out.println(articles.size());
			for (Article article : articles) {
				System.out.println(article.getUser().toString());
				
				System.out.println(article.getTitle() + ":"
						+ article.getContent() + ":作者是:"
						+ article.getUser().getUserName() + ":地址:"
						+ article.getUser().getUserAddress());
			}
		} finally {
			session.close();
		}
	}
}
5.测试结果



也可以在已经定义好user的resultMap的情况下,在association处直接使用

<association property="user" javaType="User" resultMap="resultListUser" 
			/>
来做关联查询,这个大神博文中有,我就不做解释了。


mybatis实战教程(mybatis in action)之五:与spring3集成
mybatis实战教程(mybatis in action)之六:与Spring MVC 的集成
mybatis SqlSessionDaoSupport的使用

在大神的博文中,这三个是分开写的,这里我就整合到一起了,使用spring,一般也会用springmvc。然后SqlSessionDaoSupport这个的话,我觉得没必要去在乎,可以直接实现dao就可以了,具体实现如下:

具体的项目结构如下图所示:


1.修改web.xml来进行spring和pringmvc的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
	
	<!-- log4j配置文件 -->
	<context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:/config/log4j.properties</param-value>
    </context-param>
	
	<!-- Spring的log4j监听器 -->
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
	
	
	<!--配置参数 和具体项目没有关系 在容器初始化项目还没有启动执行-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:/config/*.xml
		</param-value>
	</context-param>
	<!-- 维护ioc  -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- 可以使用RequestContextHolder.currentRequestAttributes() 获取到请求的attr -->
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    
	<!-- 前端核心分发器 -->
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!--业务控制器配置文件 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:/config/spring-mvc-servlet.xml</param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<!-- 
		<url-pattern>*.do</url-pattern>
		 -->
		 <!-- 配置为/ 不带文件后缀,会造成其它静态文件(js,css等)不能访问。如配为*.do,则不影响静态文件的访问 -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- pushlet 推送 
    <servlet>
        <servlet-name>pushlet</servlet-name>
        <servlet-class>
            nl.justobjects.pushlet.servlet.Pushlet
        </servlet-class>
        <load-on-startup>3</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>pushlet</servlet-name>
        <url-pattern>/pushlet.srv</url-pattern>
    </servlet-mapping>
    -->
	<!-- 字符集 过滤器  -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
	
	<error-page>
		<error-code>500</error-code>
		<location>/500.html</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/404.html</location>
	</error-page>
	
</web-app>

2.配置config包下的applicationContext.xml的内容,这个xml文件包含了所有关于spring的配置。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
		http://www.springframework.org/schema/jee 
		http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
	default-lazy-init="false">
	<!-- 加载properties文件到spring中,方便在spring中使用 -->
	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:/config/jdbc.properties</value>
			</list>
		</property>
	</bean>

	<!-- 使用jdbc来链接数据库,创建数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${connection.driverClassName}" />
		<property name="url" value="${connection.url}" />
		<property name="username" value="${connection.username}" />
		<property name="password" value="${connection.password}" />
		<property name="maxActive" value="${connection.maxActive}" />
		<property name="maxIdle" value="${connection.maxIdle}" />
		<property name="minIdle" value="${connection.minIdle}" />
		<property name="removeAbandoned" value="${connection.removeAbandoned}" />
		<property name="removeAbandonedTimeout" value="${connection.removeAbandonedTimeout}" />
		<property name="logAbandoned" value="${connection.logAbandoned}" />
		<property name="defaultAutoCommit" value="${connection.defaultAutoCommit}" />
		<property name="defaultReadOnly" value="${connection.defaultReadOnly}" />
	</bean>

	<!--配置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>


	<!-- 这里我在原来博文的基础上加入了spring的aop事务管理 -->
	<aop:config>
		<!-- 设置pointCut表示哪些方法要加入事务处理 -->
		<!-- 以下的事务是声明在DAO中,但是通常都会在Service来处理多个业务对象逻辑的关系,注入删除,更新等,此时如果在执行了一个步骤之后抛出异常 
			就会导致数据不完整,所以事务不应该在DAO层处理,而应该在service,这也就是Spring所提供的一个非常方便的工具,声明式事务 -->
		<aop:pointcut id="txPointcut"
			expression="execution(* com.mpc.*.service..*.*(..))" />
		<!-- 通过advisor来确定具体要加入事务控制的方法 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
	</aop:config>
	<!-- 配置哪些方法要加入事务控制 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="get*" propagation="REQUIRED" read-only="true" />
			<tx:method name="count*" propagation="REQUIRED" read-only="true" />
			<tx:method name="find*" propagation="REQUIRED" read-only="true" />
			<tx:method name="list*" propagation="REQUIRED" read-only="true" />
			<tx:method name="*" propagation="REQUIRED" read-only="true" />
			<!-- 一下方法都是可能设计修改的方法,无法设置为只读 -->
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="create*" propagation="REQUIRED" />
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="merge*" propagation="REQUIRED" />
			<tx:method name="del*" propagation="REQUIRED" />
			<tx:method name="remove*" propagation="REQUIRED" />
			<tx:method name="put*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<!-- 这个hhh是我测试在aop事务和mybatis配合的时候,事务是否生效 -->
			<tx:method name="hhh*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>



	<!-- 下面是mybatis的相关配置,这些都需要导入jar包,mybaits-spring.jar,这个在我参考的博文中有,我就不多说了 -->


	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!--dataSource属性指定要用到的连接池 -->
		<property name="dataSource" ref="dataSource" />
		<!--configLocation属性指定mybatis的核心配置文件 -->
		<property name="configLocation" value="classpath:/config/Configuration.xml" />
		<!-- 这里指定了我们的User.xml这类规定数据库操作的xml文件的位置,在项目中独立把他们放到了一个mapper包下 -->
		<property name="mapperLocations" value="classpath*:com/mpc/mybaits/mapper/*.xml" />
	</bean>
	<!-- 这个就是用接口变成的方式中,指定从哪里扫描我们的接口,托管到spring中管理,这样在spring的service或dao中就可以使用了 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.mpc.mybaits.inter" />
	</bean>



	<!-- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> 
		sqlSessionFactory属性指定要用到的SqlSessionFactory实例 <property name="sqlSessionFactory" 
		ref="sqlSessionFactory" /> mapperInterface属性指定映射器接口,用于实现此接口并生成映射器对象 <property 
		name="mapperInterface" value="com.mpc.mybaits.inter.IUserOperation" /> </bean> -->

	<context:component-scan base-package="com.mpc.mybaits">
	<!-- 注意在这里扫描的时候不要扫描controller,因为在这里装载了controller的话 controller获得的service和dao就是没有事务的-->
		<context:exclude-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
		<!-- secuirty模块需要 -->
		<context:exclude-filter type="annotation"
			expression="org.springframework.web.bind.annotation.ControllerAdvice" />
	</context:component-scan>
</beans>
3.配置config下的spring-mvc-servlet.xml,这xml文件是用来配置springmvc的,主要包括视图的解析以及相关请求的配置

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.2.xsd
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
		http://www.springframework.org/schema/jee 
		http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
	default-lazy-init="false">
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
	<!-- 默认的视图解析器 -->
	<bean id="defaultViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:order="3">
		<!-- 如果使用freemaker 或者velocity 需要更改viewClass -->
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="contentType" value="text/html" />
		<property name="prefix" value="/WEB-INF/view/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- 开启controller注解支持 -->
	<!-- 注:如果base-package=com.mpc 则注解事务不起作用 TODO 读源码 -->
	<context:component-scan base-package="com.mpc.mybaits"
		use-default-filters="false">
		<!-- 在这里扫描controller,controller就能获得有事务的service和dao -->
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
		<!-- security 模块需要 -->
		<context:include-filter type="annotation"
			expression="org.springframework.web.bind.annotation.ControllerAdvice" />
	</context:component-scan>
	<!-- 开启注解 -->
	<mvc:annotation-driven />
	<!-- 在配置为/的时候,静态文件的访问会有问题,配置了这个以后,就不会有问题了 -->
	<mvc:default-servlet-handler />
</beans>

4.config下的Configuration.xml,这个就是我们的mybatis的核心配置文件,是session的配置信息,由于很多功能托管给了spring,所以变的很瘦小

<?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>
	<typeAliases>
		<typeAlias alias="User" type="com.mpc.mybaits.model.User" />
		<typeAlias alias="Article" type="com.mpc.mybaits.model.Article" />
	</typeAliases>



	<!-- 数据源的相关配置已经托管给了spring -->
	<!-- <environments default="development"> <environment id="development"> 
		<transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" 
		value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybaits" 
		/> <property name="username" value="root" /> <property name="password" value="root" 
		/> </dataSource> </environment> </environments> -->
	<!-- mappers的配置托管给了spring -->
	<!-- <mappers> <mapper resource="com/mpc/mybaits/model/User.xml" /> </mappers> -->
</configuration>

5.DAO层的实现

package com.mpc.mybaits.dao;

import java.util.List;

import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;

public interface UserDAO {
	public List<Article> getUserArticles(int userid);

	public User hhhh(User user);
}


package com.mpc.mybaits.dao.impl;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Repository;

import com.mpc.mybaits.dao.UserDAO;
import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;

//规定这个是dao层的组件
@Repository
public class UserDaoImpl implements UserDAO {

	// 注入我们在inter中实现的接口
	@Resource
	private IUserOperation iUserOperation;

	@Override
	public List<Article> getUserArticles(int userid) {
		// 通过在dao中直接使用我们在inter中实现的接口就可以在遵循spring的规范来使用mybaits了
		return iUserOperation.getUserArticles(userid);
	}

	@Override
	public User hhhh(User user) {

		iUserOperation.addUser(user);
		return user;
	}

}

6.service层的实现
package com.mpc.mybaits.service;

import java.util.List;

import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;

public interface UserService {
	public List<Article> getUserArticles(int userid);

	public User hhhh(User user);
}

package com.mpc.mybaits.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.mpc.mybaits.dao.UserDAO;
import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;
import com.mpc.mybaits.service.UserService;

@Service
public class UserServiceImpl implements UserService {
	@Resource
	private UserDAO userDao;

	@Override
	public List<Article> getUserArticles(int userid) {
		return userDao.getUserArticles(userid);
	}

	@Override
	public User hhhh(User user) {
		// TODO Auto-generated method stub
		return userDao.hhhh(user);
	}

}

7.controller层的实现

package com.mpc.mybaits.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.mpc.mybaits.inter.IUserOperation;
import com.mpc.mybaits.model.Article;
import com.mpc.mybaits.model.User;
import com.mpc.mybaits.service.UserService;
import com.mpc.mybaits.utils.PageInfo;

/**
 * @author Administrator
 *
 */
@Controller
@RequestMapping("/article")
public class UserController {


	@Resource
	UserService userService;



	@RequestMapping("/listspring")
	@ResponseBody
	public List<Article> listallSpring(HttpServletRequest request,
			HttpServletResponse response) {

		List<Article> articles = userService.getUserArticles(1);
		return articles;

	}}

8.启动项目,测试结果:


mybatis实战教程(mybatis in action)之七:实现mybatis分页

1.按照我参考的博文的相关内容给util包中添加PageInfo、PagePlugin、ReflectHelper这三个类以后,就给代码中添加了分页插件。

2.在user.xml中添加如下代码

	<!-- 分页查询测试 -->
	<select id="selectArticleListPage" resultMap="resultUserArticleList">
		select
		user.id,user.userName,user.userAddress,article.id
		aid,article.title,article.content from user,article
		where
		user.id=article.userid and user.id=#{userid}
	</select>

3.给inter包的接口中添加相应的方法

	public List<Article> selectArticleListPage(@Param("page") PageInfo page,
			@Param("userid") int userid);
4.在Configuration.xml中添加插件

	<plugins>
		<plugin interceptor="com.mpc.mybaits.utils.PagePlugin">
			<property name="dialect" value="mysql" />
			<property name="pageSqlId" value=".*ListPage.*" />
		</plugin>
	</plugins>
这样所有包含ListPage的mapper中的方法都会执行分页动作。

5.在controller中添加如下方法测试

	@RequestMapping("/listpage")
	public @ResponseBody List<Article> listpage(HttpServletRequest request,
			HttpServletResponse response) {
		int currentPage = (request.getParameter("page") == null || Integer
				.parseInt(request.getParameter("page")) <= 0) ? 1 : Integer
				.parseInt(request.getParameter("page"));
		int pageSize = 2;

		int currentResult = (currentPage - 1) * pageSize;

		System.out.println(request.getRequestURI());
		System.out.println(request.getQueryString());

		PageInfo info = new PageInfo();

		info.setShowCount(pageSize);
		info.setCurrentResult(currentResult);
		List<Article> list = userMapper.selectArticleListPage(info, 1);

		System.out.println(info);

		int totalCount = info.getTotalResult();

		int lastPage = 0;

		if (totalCount % pageSize == 0) {
			lastPage = totalCount / pageSize;
		} else {
			lastPage = 1 + totalCount / pageSize;
		}

		if (currentPage >= lastPage) {
			currentPage = lastPage;
		}

		String pageStr = "";

		// pageStr = String.format(
		// "<a href=\"%s\">上一页</a>    <a href=\"%s\">下一页</a>",
		// request.getRequestURI() + "?page=" + (currentPage - 1),
		// request.getRequestURI() + "?page=" + (currentPage + 1));
		//
		// ModelAndView mav = new ModelAndView("list");
		// mav.addObject("articles", list);
		// mav.addObject("pageStr", pageStr);
		// return mav;
		return list;
	}

6.测试结果

我规定的是一页显示两条,查询结果如下:



确实是两条,而我的数据库中有3条数据的。

mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句(这部分貌似和分页一样··没有自己的一些感觉,只是在照猫画虎的学习一些语法来完成动态语句,参照的博客原文中介绍的都很详细了,这里我只是想记录一下自己足迹,不爱的同学可以跳过这一段)

1.mybatis if语句处理

在User.xml中添加如下代码,用来测试if语句

	<select id="selectUsersInCondition" parameterType="User"
		resultMap="resultListUser">
		select * from user where
		<if test="userAge !=null">
			userAge=#{userAge}
		</if>
		<!-- 自己修改修改··因为如果传入的userAge是null的话,语句就变成了 and userAddress=xxxx,所以多了些判断,往后才知道·原来这种不靠谱的情况有其他解决方式 -->
		<if test="userAddress !=null and userAge!=null">
			and userAddress=#{userAddress}
		</if>
		<if test="userAddress !=null and userAge==null">
			userAddress=#{userAddress}
		</if>
	</select>

在IUserOperation中添加相应的接口方法

public List<User> selectUsersInCondition(User user);


在controller中添加测试方法

	/**
	 * @return 测试if动态查询
	 */
	@RequestMapping("/listusercon")
	@ResponseBody
	public List<User> listUserCon() {
		User user = new User();

		// user.setUserAge("100");
		user.setUserAddress("shanghai,pudong");
		return userMapper.selectUsersInCondition(user);

	}

测试结果


2.choose (when,otherwize)只走其中一条路

在User.xml中添加如下代码

	<select id="dynamicChooseTest" parameterType="User" resultMap="resultListUser">
		select * from user where
		<choose>
			<when test="userAddress != null">
				userAddress=#{userAddress}
			</when>
			<when test="userAge != null">
				userAge = #{userAge}
			</when>
			<otherwise>
				id=1
			</otherwise>
		</choose>
	</select>

当传如的user对象的userAddress不为空就根据userAddress查询,userAge不为空就根据userAge查询,otherwise在所有的when都不满足的时候生效。但是永远只能选一个。

在IUserOperation中添加如下语句

public List<User> dynamicChooseTest(User user);
测试方法

	@RequestMapping("/listuserwhere")
	@ResponseBody
	public List<User> listUserWhere() {
		User user = new User();
		// user.setUserAge("100");
		// user.setUserAddress("shanghai,pudong");
		return userMapper.dynamicChooseTest(user);
	}

测试结果


可以看到的是在userAge,userAddress都为空的情况下,根据id=1来查询的

3.trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)

trim的作用就是可以为<trim></trim>标签所包含的内容加上一个前缀或者后缀,并且可以指定这个前缀或者后缀覆盖住指定的字符串。

在User.xml中添加如下代码

	<select id="dynamicTrimTest" parameterType="User" resultMap="resultListUser">
		select * from user
		<trim prefix="where" prefixOverrides="and |or">
			<if test="userAge != null">
				and userAge = #{userAge}
			</if>
			<if test="userAddress != null">
				and userAddress = #{userAddress}
			</if>
		</trim>
	</select>
这样写的话,就算是userAge不是空的,那么出现在where后面的也是 userAge=xxx不是and userAge=xxx;

在IUserOperation中添加对应的接口方法;

public List<User> dynamicTrimTest(User user);

测试方法

	@RequestMapping("/listusertrim")
	@ResponseBody
	public List<User> listUserTrim() {
		User user = new User();
		// user.setUserAge("110");
		user.setUserAddress("shanghai,pudong");
		return userMapper.dynamicTrimTest(user);
	}

测试结果



可以看到,并没有报sql语句错误,trim是成功的。

4. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or 条件)

个人理解就是<where> ====<trim prefix="where" prefixOverrides="and |or">

在User.xml中添加如下代码

	<select id="dynamicWhereTest" parameterType="User" resultMap="resultListUser">
		select * from user
		<where>
			<if test="userAge != null">
				and userAge = #{userAge}
			</if>
			<if test="userAddress != null">
				and userAddress = #{userAddress}
			</if>
		</where>
	</select>

在IUserOperation中添加对应的接口方法

public List<User> dynamicWhereTest(User user);

测试方法:

	@RequestMapping("/listuserauto")
	@ResponseBody
	public List<User> listUserAuto() {
		User user = new User();
		// user.setUserAge("110");
		user.setUserAddress("shanghai,pudong");
		return userMapper.dynamicWhereTest(user);
	}

测试结果



5.set (主要用于更新时)

在User.xml中添加如下代码

	<update id="dynamicSetTest" parameterType="User">
		update user
		<set>
			<if test="userAge != null">
				userAge = #{userAge},
			</if>
			<if test="userAddress != null">
				userAddress = #{userAddress},
			</if>
		</set>
		where id = #{id}
	</update>
<set>可以智能处理最后一个多出来的逗号

在IUserOperation中添加对应的接口方法

public void dynamicSetTest(User user);

测试方法

	@RequestMapping("/dynamicSetTest")
	@ResponseBody
	public User dynamicSetTest() {
		User user = new User();
		user.setId(1);
		user.setUserAge("120");
		user.setUserAddress("shanghai,pudong,laolao");
		userMapper.dynamicSetTest(user);
		return user;
	}


6. foreach (在实现 mybatis in 语句查询时特别有用) 

6.1但参数List类型

在User.xml中的配置内容如下:

	<select id="dynamicForeachTest" resultMap="resultUserArticleList">
		select
		user.id,user.userName,user.userAddress,user.userAge,article.id
		aid,article.title,article.content from user,article
		where
		user.id=article.userid and article.userid in
		<foreach collection="list" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>

在IUserOperation中添加对应的接口方法

public List<Article> dynamicForeachTest(List<Integer> ids);
测试方法

	@RequestMapping("/dynamicForeachTest")
	@ResponseBody
	public List<Article> dynamicForeachTest() {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(3);
		return userMapper.dynamicForeachTest(list);
	}

测试结果



6.2数组类型的参数

在User.xml添加如下代码

	<select id="dynamicForeach2Test" resultMap="resultUserArticleList">
		select
		user.id,user.userName,user.userAddress,user.userAge,article.id
		aid,article.title,article.content from user,article
		where
		user.id=article.userid and article.userid in
		<foreach collection="array" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>

在IUserOperation中添加对应的接口方法

public List<Article> dynamicForeach2Test(int[] ids);

测试方法

	@RequestMapping("/dynamicForeach2Test")
	@ResponseBody
	public List<Article> dynamicForeach2Test() {
		int[] ints = new int[] { 1, 3 };
		return userMapper.dynamicForeach2Test(ints);
	}

测试结果


只是查询参数用数组来传入了,查询结果和list传入参数的结果是一样的

6.3map类型的参数,用map类型的参数可以传入多个参数,其中一个用来迭代

在User.xml中添加如下代码,使用map来传递参数,那么参数的key值是很有用的,通过id这个key来获得id的value值,通过指定collection为ids,从而迭代ids这个key所指定的value值。

	<select id="dynamicForeach3Test" resultMap="resultUserArticleList">
		select
		user.id
		uid,user.userName,user.userAddress,user.userAge,article.id
		aid,article.title,article.content from user,article
		where
		article.userid=#{id}
		and user.id=article.userid and article.userid in
		<foreach collection="ids" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>

在IUserOperation中添加对应的接口方法

public List<Article> dynamicForeach3Test(Map<String, Object> params);

测试方法

	@RequestMapping("/dynamicForeach3Test")
	@ResponseBody
	public List<Article> dynamicForeach3Test() {
		Map<String, Object> map = new HashMap<String, Object>();
		int[] ints = new int[] { 1, 3 };
		map.put("id", 1);
		map.put("ids", ints);
		return userMapper.dynamicForeach3Test(map);
	}

测试结果


mybatis实战教程(mybatis in action)之九:mybatis 代码生成工具的使用

这方面的内容按照参考博客中大神的方法,把各种信息本地化成自己的,就直接运行程序后,我们的各种类和xml文件就自动生成到自己的包里了,这里我就不烦人了。


总结:通过参照高手的博客,可以说对mybatis有了一个很基础的入门,知道了基本的使用技能,很多的使用技巧和高级的使用方法在以后项目的实践中还需要自己慢慢的积累和摸索。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值