SpringJpa事务巨坑被踩下 The given id must not be null!

Owen Jia 2019年02月26日 252次浏览

[rent] 2019-02-22 18:09:41,751 ERROR [DubboServerHandler-172.20.30.57:20880-thread-18] com.company.rent.platform.service.dubbo.RentDubboExceptionHandler.aroudMethod(63) | dubbo service has a rentDubboException that is The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!.

上面这段错误是dubbo的接口的service在save时总数报错,而同样的接口在一般rest接口的service的save保存可以成功。

Connection is read-only. Queries leading to data modification are not allowed

当我解决dubbo的事务没有办法提交时,发现一般的rest的service的save保存一致提示上面的错误。

到此刻:一头两个大;


原因就是事务配置这块有问题,怎么搞呢?

为了调试dubbo接口顺便查找了dubbo如何本地调试,在reference中加入:url="dubbo://localhost:20881",就能解决。原本的dubbo2.5.3被我升级到了2.6.0。

搞了3天终于解决这两块问题,一定要记录一下此次踩的坑;

场景

把一个传统tomcat方式运行的项目改造成了springboot方式运行,需要重新配置数据源、事务、dubbo、spring、springmvc等等工作。

项目最终运行环境:springboot1.5.2\dubbo2.6.0\spring4.3.5\springdatajpa1.10.5。

最终代码配置

SpringApplication配置

@SpringBootApplication
@Import(SpringContextHolder.class)
@ComponentScan(basePackages = {"com.company"},excludeFilters = { @ComponentScan.Filter(value = Controller.class)})
@ServletComponentScan({"com.company.rent.platform"})
@PropertySource(value = "classpath:application.properties",ignoreResourceNotFound = true)
@ImportResource({"classpath:dubbo-consumer.xml","classpath:dubbo-producer.xml"})
@EnableTransactionManagement
public class RentApplication extends SpringBootServletInitializer {

    static ConfigurableApplicationContext cac;

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(RentApplication.class);
    }

    public static void main(String[] args){
        SpringApplication app = new SpringApplicationBuilder(RentApplication.class).build();
        cac = app.run(args);
    }
}

Dubbo消费者配置

<dubbo:application name="rent"/>
<dubbo:registry address="${dubbo.zookeeperAddress}" register="true"/>
<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.isesol"/>
<dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>

<!-- Rent引入平台基础服务 -->
<dubbo:reference id="jpushDubboService" interface="com.company.service.JpushDubboService"/>
<dubbo:reference id="jpushMsgService" interface="com.company.membercenter.dubbo.v2.service.JpushMsgService"/>
<dubbo:reference id="rentApplyService" interface="com.smtcl.iplatform.machinearchive.rentservices.IRentApplyService"/>
<dubbo:reference id="rentPlatformService" interface="com.company.factorycloud.dubbo.rent.IRentPaltformService"/>
<dubbo:reference id="memberCenterService" interface="com.company.service.MemberCenterService"/>
<dubbo:reference id="memberServiceV2" interface="com.company.membercenter.dubbo.v2.service.MemberService"/>
<dubbo:reference id="memberInfoServiceV2" interface="com.company.membercenter.dubbo.v2.service.MemberInfoService"/>
<dubbo:reference id="memberManagerService" interface="com.company.membercenter.dubbo.v2.service.MemberManagerService"/>
<dubbo:reference id="verifyCodeService" interface="com.company.membercenter.dubbo.v2.service.VerifyCodeService"/>
<dubbo:reference id="userOperateMemberService" interface="com.company.membercenter.dubbo.v2.service.UserOperateMemberService"/>
<dubbo:reference id="phoneNumRuleService" interface="com.company.membercenter.dubbo.v2.service.PhoneNumRuleService"/>
<dubbo:reference id="userCenterService" interface="com.company.service.UserCenterService"/>

<dubbo:reference id="iAuthService" interface="com.company.authority.api.service.IAuthService"/>
<dubbo:reference id="iButtonService" interface="com.company.authority.api.service.IButtonService"/>
<dubbo:reference id="iModuleService" interface="com.company.authority.api.service.IModuleService"/>
<dubbo:reference id="iOrgGroupService" interface="com.company.authority.api.service.IOrgGroupService"/>
<dubbo:reference id="iPageService" interface="com.company.authority.api.service.IPageService"/>
<dubbo:reference id="iRoleService" interface="com.company.authority.api.service.IRoleService"/>
<dubbo:reference id="iUserService" interface="com.company.authority.api.service.IUserService"/>
<dubbo:reference id="uploadTokenService" interface="com.company.support.service.UploadTokenService"/>
<dubbo:reference id="factoryService" interface="com.company.cngl.service.dubbo.IFactoryService"/>
<dubbo:reference id="tokenVerifyService" interface="com.company.service.lgn.TokenVerifyService"/>

<dubbo:reference interface="com.company.rent.service.IDemoService" url="dubbo://localhost:20881" check="false"></dubbo:reference>

Dubbo生产者配置

<?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:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://code.alibabatech.com/schema/dubbo
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    ">
    <!-- Rent 提供的服务 -->
    <dubbo:service interface="com.company.rent.service.IRentMtService" ref="iRentMtService"/>
    <dubbo:service interface="com.company.rent.service.IDemoService" ref="iDemoService"/>
    <dubbo:service interface="com.company.rent.service.IRentCustomerService" ref="iRentCustomerService"/>
    <dubbo:service interface="com.company.rent.service.IRentOrderService" ref="iRentOrderService"/>
    <dubbo:service interface="com.company.rent.service.IRentCompanyService" ref="iRentCompanyService"/>
    <dubbo:service interface="com.company.rent.service.IRentBillService" ref="iRentBillService"/>
    <dubbo:service interface="com.company.rent.service.IRentContractService" ref="iRentContractService"/>
    <dubbo:service interface="com.company.rent.service.IRentMtDataService" ref="iRentMtDataService"/>

</beans>

数据源及事务配置

/**
 * 自定义数据源配置
 * @author: Owen Jia
 * @time: 2018/12/20 13:49
 */
@Configuration
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = "com.isesol")
@EnableAspectJAutoProxy
public class DataSourceConfig {

    @Value("${jdbc.driver}")
    String driverClassName;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;
    @Value("${jdbc.pool.maxActive}")
    int maxActive;
    @Value("${jdbc.pool.initialSize}")
    int initialSize;
    @Value("${jdbc.pool.maxWait}")
    int maxWait;
    @Value("${jdbc.pool.minIdle}")
    int minIdle;

    @Bean(name = "dataSource",autowire = Autowire.BY_TYPE)
    public DataSource initDurid() {
        DruidDataSource druid = new DruidDataSource();
        druid.setUrl(url);
        druid.setDriverClassName(driverClassName);
        druid.setUsername(username);
        druid.setPassword(password);

        try {
            druid.setFilters("stat");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        druid.setMaxActive(maxActive);
        druid.setInitialSize(initialSize);
        druid.setMaxWait(maxWait);
        druid.setMinIdle(minIdle);

        druid.setValidationQuery("select user()");
        druid.setTimeBetweenEvictionRunsMillis(180000);
        druid.setMinEvictableIdleTimeMillis(300000);
        druid.setTestWhileIdle(true);
        druid.setTestOnBorrow(true);
        druid.setTestOnReturn(false);

        //配置druid连接超时占用强制回收,owen jia at 20190211
        druid.setRemoveAbandoned(true);
        druid.setRemoveAbandonedTimeout(600);
        druid.setLogAbandoned(true);

        druid.setPoolPreparedStatements(true);
        druid.setMaxOpenPreparedStatements(200);

        druid.setProxyFilters(Lists.newArrayList(initLogFilter(),initStatFilter()));
        return druid;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate initJdbcTemplate(){
        return new JdbcTemplate(initDurid());
    }

    @Bean(name = "log-filter")
    public Slf4jLogFilter initLogFilter(){
        Slf4jLogFilter filter = new Slf4jLogFilter();
        filter.setDataSourceLogEnabled(true);
        filter.setConnectionLogEnabled(true);
        filter.setResultSetLogEnabled(true);
        filter.setStatementExecutableSqlLogEnable(true);
        filter.setStatementLogEnabled(true);
        return filter;
    }

    @Bean(name = "stat-filter")
    public StatFilter initStatFilter(){
        StatFilter statFilter = new StatFilter();
        statFilter.setSlowSqlMillis(180000);
        statFilter.setLogSlowSql(true);
        statFilter.setMergeSql(true);
        statFilter.setConnectionStackTraceEnable(true);
        return statFilter;
    }

    @Value("${hibernate.hbm2ddl.auto}")
    String hbm2ddlAuto;
    @Value("${hibernate.show_sql}")
    String showSql;
    @Value("${hibernate.format_sql}")
    String formatSql;

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean initFactoryBean() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(initDurid());
        factoryBean.setJpaVendorAdapter(initHibernateJpaVendorAdapter());
        factoryBean.setPersistenceUnitName("default");
        factoryBean.setPackagesToScan("com.company.rent");
        Properties jpaProp = new Properties();
        jpaProp.setProperty("hibernate.hbm2ddl.auto",hbm2ddlAuto);
        jpaProp.setProperty("hibernate.show_sql",showSql);
        jpaProp.setProperty("hibernate.format_sql",formatSql);
        jpaProp.setProperty("hibernate.physical_naming_strategy", ImprovedNamingStrategy.class.getName());
        // Owen Jia at 20190211
        //配置连接释放模式:after_statement 请求执行后释放,after_transaction 事务提交后释放,on_close session主动关闭释放
        jpaProp.setProperty("hibernate.connection.release_mode","after_transaction");//"on_close"
        factoryBean.setJpaProperties(jpaProp);
        return factoryBean;
    }

    @Bean(name = "hibernateJpaVendorAdapter")
    public HibernateJpaVendorAdapter initHibernateJpaVendorAdapter(){
        HibernateJpaVendorAdapter jpaVendor = new HibernateJpaVendorAdapter();
        jpaVendor.setDatabasePlatform(Hibernates.getDialect(initDurid()));
        return jpaVendor;
    }

    @Bean(name = "transactionManager")
    public JpaTransactionManager initJapTransactionM(){
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(initFactoryBean().getNativeEntityManagerFactory());
        return jpaTransactionManager;
    }

    @Bean(name = "transactionTemplate")
    public TransactionTemplate initTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(initJapTransactionM());
        return transactionTemplate;
    }
}

Yaml文件配置,只给出相关特别配置,其他都是常规

debug=true

logging.config=classpath:log4j2.xml

spring.profiles.active=${ENV:dev}
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

server.context-path=/
server.port=8080
server.session.timeout=20
server.tomcat.uri-encoding=utf-8

作者:Owen Jia
推荐关注他的博客:Owen Blog,里面大量优质博文。