Spring + Jta +JDBCTemplate 分布式事物实现方式

来源:互联网 发布:福建天正网络咨询 编辑:程序博客网 时间:2024/05/18 04:58

最近项目中需要用到多数据源管,数据访问层采用的是JDBCTemplate去做的,一开始是在数据源这块做了一个多数据源的操作类,通过拦截器注解去动态赋值指定的数据源操作,这种做法在查询中是没有问题的,但是DML操作时,会出现问题:事物中无法动态操作数据源,导致很多操作指针对第一个库。查询资料的时候发现:
DataSourceTransactionManager这个事物管理类只针对单个数据源进行事物控制.

解决的方案也有多种,这里只提及我实践过的两种:
  1. 开启多个数据源去进行操作,这种是可以自己去实现的.
  2. 利用JTA和Atomikos的多数据源分布事物管理

方案一:

思路:
- 自定义一个注解类,通过传递参数告诉这个业务需要用到哪几个数据源
- 然后仿照Spring中的@Transactional的实现模式,去构建一个集合来开启多个事物
- 然后通过拦截器去动态分配业务给这个集合告诉他,要开几个事物

代码:
applicationContext-datasource.xml

<!-- 数据源1 --> <bean id="mvp" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">        <property name="driverClassName" value="${mvp.driverClassName}"></property>        <property name="url" value="${mvp.url}"></property>        <property name="username" value="${mvp.username}"></property>        <property name="password" value="${mvp.password}"></property>        <property name="filters" value="${mvp.filters}"/>        <!-- 配置初始化大小、最小、最大 -->        <property name="initialSize" value="${mvp.initialSize}"/>        <property name="minIdle" value="${mvp.minIdle}"/>        <property name="maxActive" value="${mvp.maxActive}"/>        <!-- 配置获取连接等待超时的时间 -->        <property name="maxWait" value="${mvp.maxWait}"/>        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->        <property name="timeBetweenEvictionRunsMillis" value="${mvp.timeBetweenEvictionRunsMillis}"/>        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->        <property name="minEvictableIdleTimeMillis" value="${mvp.minEvictableIdleTimeMillis}"/>        <property name="testWhileIdle" value="${mvp.testWhileIdle}"/>        <property name="testOnBorrow" value="${mvp.testOnBorrow}"/>        <property name="testOnReturn" value="${mvp.testOnReturn}"/>        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->        <property name="poolPreparedStatements" value="${mvp.poolPreparedStatements}"/>        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>        <property name="removeAbandoned" value="${mvp.removeAbandoned}" />        <property name="removeAbandonedTimeout" value="${mvp.removeAbandonedTimeout}" />        <property name="maxOpenPreparedStatements" value="${mvp.maxOpenPreparedStatements}" />        <property name="logAbandoned" value="${mvp.logAbandoned}"/>        <property name="queryTimeout" value="${mvp.querytimeout}"/>        <property name="validationQuery" value="${mvp.validationQuery}"/>    </bean>    <!-- 数据源2 -->    <bean id="system" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">        <property name="driverClassName" value="${system.driverClassName}"></property>        <property name="url" value="${system.url}"></property>        <property name="username" value="${system.username}"></property>        <property name="password" value="${system.password}"></property>        <property name="filters" value="${system.filters}"/>        <!-- 配置初始化大小、最小、最大 -->        <property name="initialSize" value="${system.initialSize}"/>        <property name="minIdle" value="${system.minIdle}"/>        <property name="maxActive" value="${system.maxActive}"/>        <!-- 配置获取连接等待超时的时间 -->        <property name="maxWait" value="${system.maxWait}"/>        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->        <property name="timeBetweenEvictionRunsMillis" value="${system.timeBetweenEvictionRunsMillis}"/>        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->        <property name="minEvictableIdleTimeMillis" value="${system.minEvictableIdleTimeMillis}"/>        <property name="testWhileIdle" value="${system.testWhileIdle}"/>        <property name="testOnBorrow" value="${system.testOnBorrow}"/>        <property name="testOnReturn" value="${system.testOnReturn}"/>        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->        <property name="poolPreparedStatements" value="${system.poolPreparedStatements}"/>        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>        <property name="removeAbandoned" value="${system.removeAbandoned}" />        <property name="removeAbandonedTimeout" value="${system.removeAbandonedTimeout}" />        <property name="maxOpenPreparedStatements" value="${system.maxOpenPreparedStatements}" />        <property name="logAbandoned" value="${system.logAbandoned}"/>        <property name="queryTimeout" value="${system.querytimeout}"/>        <property name="validationQuery" value="${system.validationQuery}"/>    </bean>    <!-- dao层访问处理工具 分别指定两个不同的datasource -->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="default"/>    </bean>    <bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="system"/>    </bean>

applicationContext-service.xml 业务层配置文件

<!-- 一些扫描包或者其他定义的业务类 我这边就不列入了 --><!-- 多数据源切面类 -->    <bean id="multiTransactionalAspect" class="com.elab.execute.utils.MultiTransactionalAspect"/>     多数据Aop配置     <aop:config proxy-target-class="true">        <!-- 定义一个切入点表达式: 拦截哪些方法 -->        <aop:aspect ref="multiTransactionalAspect">            <aop:pointcut id="pointUserMgr" expression="execution(* com.test.execute.services..*(..))"/>            <aop:around method="around" pointcut-ref="pointUserMgr" />        </aop:aspect>    </aop:config>

注解类 :

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface MultiTransactional {    String[] values() default "";     // TODO 当然你如果想做的更完善一点,可以参考@Transactional这个类,自己去做,什么传播机制啊,隔离级别啊 等等,思路是一样的}

注解实现类 - 其实这里差不多都是沿用Spring的事物实现方式:

import com.alibaba.druid.pool.DruidDataSource;import org.apache.commons.lang3.ArrayUtils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.stereotype.Component;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.DefaultTransactionDefinition;import java.lang.reflect.Method;import java.util.Stack;/** * @Description: 多数据源动态管理 * @Author: Liukx on 2017/7/28 - 16:41 */@Component("multiTransactionalAspect")public class MultiTransactionalAspect {    private Logger logger = LoggerFactory.getLogger(getClass());    @Autowired    private SpringUtils springUtils;    /**     * 切入点     *     * @param point     * @return     * @throws Throwable     */    public Object around(ProceedingJoinPoint point) throws Throwable {        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();        Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();        try {            Object target = point.getTarget();            String method = point.getSignature().getName();            Class classz = target.getClass();            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();            Method m = classz.getMethod(method, parameterTypes);            if (m != null && m.isAnnotationPresent(MultiTransactional.class)) {                MultiTransactional multiTransactional = m.getAnnotation(MultiTransactional.class);                if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, multiTransactional)) {                    return null;                }                Object ret = point.proceed();                commit(dataSourceTransactionManagerStack, transactionStatuStack);                return ret;            }            Object ret = point.proceed();            return ret;        } catch (Exception e) {            rollback(dataSourceTransactionManagerStack, transactionStatuStack);            logger.error(String.format(                    "MultiTransactionalAspect, method:%s-%s occors error:", point                            .getTarget().getClass().getSimpleName(), point                            .getSignature().getName()), e);            throw e;        }    }    /**     * 打开一个事物方法     *     * @param dataSourceTransactionManagerStack     * @param transactionStatuStack     * @param multiTransactional     * @return     */    private boolean openTransaction(            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,            Stack<TransactionStatus> transactionStatuStack,            MultiTransactional multiTransactional) {        // 获取注解中要打开的事物类型        String[] transactionMangerNames = multiTransactional.values();        if (ArrayUtils.isEmpty(multiTransactional.values())) {            return false;        }        for (String beanName : transactionMangerNames) {            // 创建一个新的事物管理器,用来管理接下来要用到的事物            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();            // 根据注解中获取到的数据标识,从spring容器中去查找对应的数据源            DruidDataSource dataSource = (DruidDataSource) springUtils.getBean(beanName);            //然后交给事物管理器去管理            dataSourceTransactionManager.setDataSource(dataSource);            // 定义一个新的事物定义            DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();            // 设置一个默认的事物传播机制,注意的是这里可以拓展注解中没有用到的属性            // defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);            // 将这个事物的定义放入Stack中            /**             * 其中为什么要用Stack来保存TransactionManager和TransactionStatus呢?             * 那是因为Spring的事务处理是按照LIFO/stack behavior的方式进行的。             * 如若顺序有误,则会报错:             */            transactionStatuStack.push(transactionStatus);            dataSourceTransactionManagerStack                    .push(dataSourceTransactionManager);        }        return true;    }    /**     * 提交事物方法实现     *     * @param dataSourceTransactionManagerStack     * @param transactionStatuStack     */    private void commit(            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,            Stack<TransactionStatus> transactionStatuStack) {        while (!dataSourceTransactionManagerStack.isEmpty()) {            dataSourceTransactionManagerStack.pop().commit(                    transactionStatuStack.pop());        }    }    /**     * 回滚事物方法实现     *     * @param dataSourceTransactionManagerStack     * @param transactionStatuStack     */    private void rollback(            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,            Stack<TransactionStatus> transactionStatuStack) {        while (!dataSourceTransactionManagerStack.isEmpty()) {            dataSourceTransactionManagerStack.pop().rollback(                    transactionStatuStack.pop());        }    }}

service 1 - 2 :

/** * 一些实验性Demo接口 * * @author Liukx * @create 2017-07-28 10:11 * @email liukx@elab-plus.com **/public interface IDemoService {    public void mvpManage() throws Exception;}/** * 一些实验性Demo接口 * * @author Liukx * @create 2017-07-28 10:11 * @email liukx@elab-plus.com **/public interface IDemoService2 {    public void beidouMange() throws CoreException;}

service实现类:

/** * 实现性Demo接口实现 * * @author Liukx * @create 2017-07-28 10:12 * @email liukx@elab-plus.com **/@Service("demoService")public class DemoServiceImpl implements IDemoService {    @Autowired    @Qualifier("demoTestDao")    private IDemoTestDao testDao;    @Autowired    private IDemoService2 demoService2;//    @Autowired//    private DataSourceTransactionManager transactionManager;    //TODO 这个注解很关键 - 必须是这个注解才能被拦截器拦截到    @MultiTransactional(values = {DataSource.mvp, DataSource.system})    public void mvpManage() throws CoreException {        System.out.println("=====================开始处理MVP");        testDao.insert();        List<Map<String, Object>> select = testDao.select();        System.out.println("===============>>>>>>>>>>>>>>>>>" + select.size());        System.out.println("=====================结束处理MVP");        demoService2.beidouMange();//        System.out.println("业务处理完成,开始报错");        int i = 1 / 0;    }}

service 2实现

/** * @author Liukx * @create 2017-07-28 11:07 * @email liukx@elab-plus.com **/@Service("demo2")public class DemoServiceImpl2 implements IDemoService2 {    @Autowired    @Qualifier("beidouTestDao")    private IDemoTestDao testDao;    @MultiTransactional(values = DataSource.system)    public void beidouMange() throws CoreException {        System.out.println("=====================开始处理北斗");        testDao.insert();        List<Map<String, Object>> select = testDao.select();        System.out.println("========================================>" + select.size());        System.out.println("=====================结束处理北斗");    }}

dao实现: 这里就只列入实现类

@Repository("beidouTestDao")public class BeiDouTestDaoImpl extends BaseDao implements IDemoTestDao {    /**     * 我们这块目前是有一些封装的,不过你不用太关注;     * 你只要把对应的数据源指定好就行了     */    @Autowired    @Qualifier("jdbcTemplate2")    public JdbcTemplate jdbcTemplate;    public int insert() throws CoreException {        LinkedHashMap<String, Object> params = new LinkedHashMap<>();        params.put("name", "某某某" + RandomUtils.randomNum(3));        params.put("created", new Date());        return executeInsert("test.insert", params);    }    @Override    public List<Map<String, Object>> select() throws CoreException {        return findList("test.findAll", new LinkedHashMap<String, Object>());    }}
@Repository("demoTestDao")public class DemoTestDaoImpl extends BaseDao implements IDemoTestDao {    /**     * 我们这块目前是有一些封装的,不过你不用太关注;     * 你只要把对应的数据源指定好就行了     */    @Autowired    @Qualifier("jdbcTemplate")    public JdbcTemplate jdbcTemplate;    public int insert() throws CoreException {        LinkedHashMap<String, Object> params = new LinkedHashMap<>();        params.put("name", "某某某" + RandomUtils.randomNum(2));        params.put("created", new Date());        return executeInsert("test.insert", params);    }    public List<Map<String, Object>> select() throws CoreException {        return findList("test.findAll", new LinkedHashMap<String, Object>());    }}

另外有一个动态获取bean的工具类:

@Componentpublic class SpringUtils implements ApplicationContextAware {    private ApplicationContext applicationContext;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    public Object getBean(String beanId) {        return applicationContext.getBean(beanId);    }}

PS : 这个方案会存在一定的问题 -> 就是如果你下一个另一个数据库操作的业务serivce方法中如果定义了@Transactional它将会又开启一个事物去执行…

方案2 : 利用JTA+Atomikios实现事物管理,业务层代码和上面差不多,主要是配置文件这块.

applicationContext-datasource.xml

<!-- 父配置 --><bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"          destroy-method="close" abstract="true">        <property name="xaDataSourceClassName"                  value="com.alibaba.druid.pool.xa.DruidXADataSource"/>  <!-- SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]  -->        <property name="poolSize" value="10"/>        <property name="minPoolSize" value="10"/>        <property name="maxPoolSize" value="30"/>        <property name="borrowConnectionTimeout" value="60"/>        <property name="reapTimeout" value="20"/>        <property name="maxIdleTime" value="60"/>        <property name="maintenanceInterval" value="60"/>        <property name="loginTimeout" value="60"/>        <property name="testQuery" value="${default.validationQuery}"/>    </bean>    <!-- 主配置 -->    <bean id="masterDataSource" parent="abstractXADataSource">        <property name="uniqueResourceName" value="masterDB"/>        <property name="xaProperties">            <props>                <prop key="driverClassName">${default.driverClassName}</prop>                <prop key="url">${default.url}</prop>                <prop key="password">${default.password}</prop>                <!--  <prop key="user">${jdbc.username}</prop> --> <!-- mysql -->                <prop key="username">${default.username}</prop>   <!-- durid -->                <prop key="initialSize">0</prop>                <prop key="maxActive">20</prop> <!-- 若不配置则代码执行"{dataSource-1} inited"此处停止  -->                <prop key="minIdle">0</prop>                <prop key="maxWait">60000</prop>                <prop key="validationQuery">${default.validationQuery}</prop>                <prop key="testOnBorrow">false</prop>                <prop key="testOnReturn">false</prop>                <prop key="testWhileIdle">true</prop>                <prop key="removeAbandoned">true</prop>                <prop key="removeAbandonedTimeout">1800</prop>                <prop key="logAbandoned">true</prop>                <prop key="filters">mergeStat</prop>            </props>        </property>    </bean>    <bean id="slaveDataSource" parent="abstractXADataSource">        <property name="uniqueResourceName" value="slaveDB"/>        <property name="xaProperties">            <props>                <prop key="driverClassName">${system.driverClassName}</prop>                <prop key="url">${system.url}</prop>                <prop key="password">${system.password}</prop>                <!--  <prop key="user">${jdbc.username}</prop> -->                <prop key="username">${system.username}</prop>                <prop key="initialSize">0</prop>                <prop key="maxActive">20</prop>                <prop key="minIdle">0</prop>                <prop key="maxWait">60000</prop>                <prop key="validationQuery">${system.validationQuery}</prop>                <prop key="testOnBorrow">false</prop>                <prop key="testOnReturn">false</prop>                <prop key="testWhileIdle">true</prop>                <prop key="removeAbandoned">true</prop>                <prop key="removeAbandonedTimeout">1800</prop>                <prop key="logAbandoned">true</prop>                <prop key="filters">mergeStat</prop>            </props>        </property>    </bean>    <!-- 将数据源交给数据库操作模版工具 -->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="masterDataSource"/>    </bean>    <bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="slaveDataSource"/>    </bean>

applicationContext-atomikos.xml

<?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:context="http://www.springframework.org/schema/context"       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd    http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-4.0.xsd    http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">    <description>配置事物</description>    <!-- atomikos事务管理器 -->    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"          destroy-method="close">        <property name="forceShutdown">            <value>true</value>        </property>    </bean>    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">        <property name="transactionTimeout" value="300"/>    </bean>    <!-- spring 事务管理器 -->    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">        <property name="transactionManager" ref="atomikosTransactionManager"/>        <property name="userTransaction" ref="atomikosUserTransaction"/>        <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->        <property name="allowCustomIsolationLevels" value="true"/>    </bean>    <aop:config proxy-target-class="true">        <aop:pointcut id="pointUserMgr" expression="execution(* com.elab.execute.services..*(..))"/>        <aop:advisor pointcut-ref="pointUserMgr" advice-ref="txAdvice"/>    </aop:config>    <!-- 通过切入点去实现事物触发机制 -->    <tx:advice id="txAdvice" transaction-manager="springTransactionManager">        <tx:attributes>            <tx:method name="get*" propagation="REQUIRED" read-only="true"/>            <tx:method name="find*" propagation="REQUIRED" read-only="true"/>            <tx:method name="has*" propagation="REQUIRED" read-only="true"/>            <tx:method name="locate*" propagation="REQUIRED" read-only="true"/>            <tx:method name="mvp*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>            <tx:method name="beidou*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>        </tx:attributes>    </tx:advice>    <!--<tx:annotation-driven transaction-manager="springTransactionManager"-->    <!--proxy-target-class="true"></tx:annotation-driven>--></beans>

pom.xml

<!-- JTA -->        <dependency>            <groupId>javax.transaction</groupId>            <artifactId>jta</artifactId>            <version>1.1</version>        </dependency>        <dependency>            <groupId>com.atomikos</groupId>            <artifactId>atomikos-util</artifactId>            <version>4.0.2</version>        </dependency>        <dependency>            <groupId>com.atomikos</groupId>            <artifactId>transactions</artifactId>            <version>4.0.2</version>        </dependency>        <dependency>            <groupId>com.atomikos</groupId>            <artifactId>transactions-jta</artifactId>            <version>4.0.2</version>        </dependency>        <dependency>            <groupId>com.atomikos</groupId>            <artifactId>transactions-jdbc</artifactId>            <version>4.0.2</version>        </dependency>        <dependency>            <groupId>com.atomikos</groupId>            <artifactId>transactions-api</artifactId>            <version>4.0.2</version>        </dependency>        <dependency>            <groupId>cglib</groupId>            <artifactId>cglib-nodep</artifactId>            <version>3.2.2</version>        </dependency>

业务层代码和方案一的差不多,看了一下大概的思路:
1. 在调用被拦截器匹配的方法时,开启一个新的事物
2. 当执行到DML操作时,会获取他对应的数据源,并且会和当前线程的事物管理器中的数据源进行匹配,如果不存在,则到连接池中获取一个新的连接,并把这个连接放入当前线程的事物管理器中进行管理
3. 当所有业务执行完毕,并且没有报错的时候,会执行一个两阶段提交的方式

    PREPARE TRANSACTION transaction_id PREPARE TRANSACTION 为当前事务的两阶段提交做准备。 在命令之后,事务就不再和当前会话关联了;它的状态完全保存在磁盘上, 它提交成功有非常高的可能性,即使是在请求提交之前数据库发生了崩溃也如此。这条命令必须在一个用BEGIN显式开始的事务块里面使用。      COMMIT PREPARED transaction_id 提交已进入准备阶段的ID为transaction_id的事务      ROLLBACK PREPARED transaction_id 回滚已进入准备阶段的ID为transaction_id的事务

推荐使用第二种方式,因为它有比较好的连接池以及相对完善的机制,第一种考虑的情况比较少,会出现问题,当然,你如果愿意折腾自己写一套,可以参考一下..

以上为个人学习参考,有什么不足的地方欢迎指正.

参考博客:
Mybatis + JTA http://blog.csdn.net/zmx729618/article/details/54344296
分布式事物提交以及JTA的概念 : http://www.jasongj.com/big_data/two_phase_commit/
JTA一些实现原理:https://www.ibm.com/developerworks/cn/java/j-lo-jta/

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 京东登录名忘了怎么办? 京东已经发货了怎么办 苹果7p黑亮掉漆怎么办 淘宝卖家不肯退运费怎么办 健身付款收据丢了怎么办 收据丢了怎么办能退款 苹果售后不承认基带问题怎么办 电话卡欠费了不用了怎么办 软件移不到sd卡怎么办 手机显示sd卡受损怎么办 美的冰箱出现e6怎么办 美的冰箱显示e6怎么办 冰箱电脑板坏了怎么办 笔记本网线接口坏了怎么办 蓝p吃了一片 怎么办 sd卡上锁了忘记密码怎么办 手机sd卡被锁定怎么办 冰箱制冷管堵了怎么办 冰箱的管子破了怎么办 淘宝京东e卡冻结怎么办 苏宁任性付冻结怎么办 苏宁订单删除了怎么办 联通销户话费有余额怎么办 暖气改地热不热怎么办 老楼房暖气不热怎么办 4s店修不好车怎么办 苏宁的发票丢了怎么办 京东退货没有发票怎么办 发票发错了邮箱怎么办 苹果手机忘记电子邮箱验证码怎么办 退差价把红包退还了怎么办 网上购票票丢了怎么办 岗位人手不够老板又不招人来怎么办 辞职后提成不发怎么办 老板给客户吵架员工该怎么办 冰柜声音大怎么办嗡嗡响 交了钱电没有怎么办 小白熊电动吸奶器显示F1怎么办 花洒的水变小了怎么办 手机插卡处坏了怎么办 吉利帝豪一键启动钥匙没电怎么办