java Spring 的JDBCTemplet批量入库数据时如果有一条数据入库不成功,其他的数据还会入库吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java Spring 的JDBCTemplet批量入库数据时如果有一条数据入库不成功,其他的数据还会入库吗相关的知识,希望对你有一定的参考价值。

比方说我要有10条数据批量入库,,如果有1条数据有问题会在入库的时候失败,那其他9条正常的数据还会成功入库吗

在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,问题就出现了:如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?问题的分析我首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQLServer、mysql等,也可能是不同的数据源:比如apache提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。但是,我很快发现一个问题:当多用户同时并发访问数据库的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知,我们在使用spring框架的时候,在beanFactory中注册的bean基本上都是采用单例模式,即spring在启动的时候,这些bean就装载到内存中,并且每个bean在整个项目中只存在一个对象。正因为只存在一个对象,对象的所有属性,更准确说是实例变量,表现得就如同是个静态变量(实际上“静态”与“单例”往往是非常相似的两个东西,我们常常用“静态”来实现“单例”)。拿我们的问题来说,sessionFactory在整个项目中只有一个对象,它的实例变量dataSource也就只有一个,就如同一个静态变量一般。如果不同的用户都不断地去修改dataSource的值,必然会出现多用户争用一个变量的问题,对系统产生隐患。通过以上的分析,解决多数据源访问问题的关键,就集中在sessionFactory在执行数据持久化的时候,能够通过某段代码去根据客户的需要动态切换数据源,并解决资源争用的问题。问题的解决(一)采用Decorator设计模式要解决这个问题,我的思路锁定在了这个dataSource上了。如果sessionFactory指向的dataSource可以根据客户的需求去连接客户所需要的真正的数据源,即提供动态切换数据源的功能,那么问题就解决了。那么我们怎么做呢?去修改那些我们要使用的dataSource源码吗?这显然不是一个好的方案,我们希望我们的修改与原dataSource代码是分离的。根据以上的分析,使用GoF设计模式中的Decorator模式(装饰者模式)应当是我们可以选择的最佳方案。什么是“Decorator模式”?简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。当我们使用Decorator的时候与原类完全一样,当Decorator的某些功能却已经修改为了我们需要修改的功能。Decorator模式的结构如图。我们本来需要修改图中所有具体的Component类的一些功能,但却并不是去直接修改它们的代码,而是在它们的外面增加一个Decorator。Decorator与具体的Component类都是继承的AbstractComponent,因此它长得和具体的Component类一样,也就是说我们在使用Decorator的时候就如同在使用ConcreteComponentA或者ConcreteComponentB一样,甚至那些使用ConcreteComponentA或者ConcreteComponentB的客户程序都不知道它们用的类已经改为了Decorator,但是Decorator已经对具体的Component类的部分方法进行了修改,执行这些方法的结果已经不同了。(二)设计MultiDataSource类现在回到我们的问题,我们需要对dataSource的功能进行变更,但又不希望修改dataSource中的任何代码。我这里指的dataSource是所有实现javax.sql.DataSource接口的类,我们常用的包括apache提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等,这些类我们不可能修改它们本身,更不可能对它们一个个地修改以实现动态分配数据源的功能,同时,我们又希望使用dataSource的sessionFactory根本就感觉不到这样的变化。Decorator模式就正是解决这个问题的设计模式。首先写一个Decorator类,我取名叫MultiDataSource,通过它来动态切换数据源。同时在配置文件中将sessionFactory的dataSource属性由原来的某个具体的dataSource改为MultiDataSource。如图:对比原Decorator模式,AbstractComponent是一个抽象类,但在这里我们可以将这个抽象类用接口来代替,即DataSource接口,而ConcreteComponent就是那些DataSource的实现类,如BasicDataSource、JndiObjectFactoryBean等。MultiDataSource封装了具体的dataSource,并实现了数据源动态切换:java代码publicclassMultiDataSourceimplementsDataSourceprivateDataSourcedataSource=null;publicMultiDataSource(DataSourcedataSource)this.dataSource=dataSource;publicConnectiongetConnection()throwsSQLExceptionreturngetDataSource().getConnection();//其它DataSource接口应当实现的方法publicDataSourcegetDataSource()returnthis.dataSource;publicvoidsetDataSource(DataSourcedataSource)this.dataSource=dataSource;客户在发出请求的时候,将dataSourceName放到request中,然后把request中的数据源名通过调用newMultiDataSource(dataSource)时可以告诉MultiDataSource客户需要的数据源,就可以实现动态切换数据源了。但细心的朋友会发现这在单例的情况下就是问题的,因为MultiDataSource在系统中只有一个对象,它的实例变量dataSource也只有一个,就如同一个静态变量一般。正因为如此,单例模式让许多设计模式都不得不需要更改,这将在我的《“单例”更改了我们的设计模式》中详细讨论。那么,我们在单例模式下如何设计呢?(三)单例模式下的MultiDataSource在单例模式下,由于我们在每次调用MultiDataSource的方法的时候,dataSource都可能是不同的,所以我们不能将dataSource放在实例变量dataSource中,最简单的方式就是在方法getDataSource()中增加参数,告诉MultiDataSource我到底调用的是哪个dataSource:java代码publicDataSourcegetDataSource(StringdataSourceName)log.debug("dataSourceName:"+dataSourceName);tryif(dataSourceName==null||dataSourceName.equals(""))returnthis.dataSource;return(DataSource)this.applicationContext.getBean(dataSourceName);catch(NoSuchBeanDefinitionExceptionex)thrownewDaoException("ThereisnotthedataSource值得一提的是,我需要的数据源已经都在spring的配置文件中注册,dataSourceName就是其对应的id。xml代码oracle.jdbc.driver.OracleDrivervalue>property>bean>oracle.jdbc.driver.OracleDrivervalue>property>bean>为了得到spring的ApplicationContext,MultiDataSource类必须实现接口org.springframework.context.ApplicationContextAware,并且实现方法:java代码privateApplicationContextapplicationContext=null;publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansExceptionthis.applicationContext=applicationContext;如此这样,我就可以通过this.applicationContext.getBean(dataSourceName)得到dataSource了。(四)通过线程传递dataSourceName查看以上设计,MultiDataSource依然无法运行,因为用户在发出请求时,他需要连接什么数据库,其数据源名是放在request中的,要将request中的数据源名传给MultiDataSource,需要经过BUS和DAO,也就是说为了把数据源名传给MultiDataSource,BUS和DAO的所有方法都要增加dataSourceName的参数,这是我们不愿看到的。写一个类,通过线程的方式跳过BUS和DAO直接传递给MultiDataSource是一个不错的设计:java代码publicclassSpObserverprivatestaticThreadLocallocal=newThreadLocal();publicstaticvoidputSp(Stringsp)local.set(sp);publicstaticStringgetSp()return(String)local.get();做一个filter,每次客户发出请求的时候就调用SpObserver.petSp(dataSourceName),将request中的dataSourceName传递给SpObserver对象。最后修改MultiDataSource的方法getDataSource():java代码publicDataSourcegetDataSource()Stringsp=SpObserver.getSp();returngetDataSource(sp);完整的MultiDataSource代码在附件中。(五)动态添加数据源通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring的ApplicationContext中,如果我在程序运行过程中动态添加数据源怎么?这确实是一个问题,而且在我们的项目中也确实遇到。spring的ApplicationContext是在项目启动的时候加载的。加载以后,我们如何动态地加载新的bean到ApplicationContext中呢?我想到如果用spring自己的方法解决这个问题就好了。所幸的是,在查看spring的源代码后,我找到了这样的代码,编写了DynamicLoadBean类,只要调用loadBean()方法,就可以将某个或某几个配置文件中的bean加载到ApplicationContext中(见附件)。不通过配置文件直接加载对象,在spring的源码中也有,感兴趣的朋友可以自己研究。(六)在spring中配置在完成了所有这些设计以后,我最后再唠叨一句。我们应当在spring中做如下配置:xml代码bean>property>bean>property>bean>其中dataSource属性实际上更准确地说应当是defaultDataSource,即spring启动时以及在客户没有指定数据源时应当指定的默认数据源。该方案的优势以上方案与其它方案相比,它有哪些优势呢?首先,这个方案完全是在spring的框架下解决的,数据源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource属性,它甚至都不知道dataSource的改变。唯一不同的是在真正的dataSource与sessionFactory之间增加了一个MultiDataSource。其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource、SpObserver两个类。MultiDataSource类真正要写的只有getDataSource()和getDataSource(sp)两个方法,而SpObserver类更简单了。实现越简单,出错的可能就越小,维护性就越高。最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS和DAO的编写。如果我们的项目在开始之初是单数据源的情况下开发,随着项目的进行,需要变更为多数据源,则只需要修改spring配置,并少量修改MVC层以便在请求中写入需要的数据源名,变更就完成了。如果我们的项目希望改回单数据源,则只需要简单修改配置文件。这样,为我们的项目将增加的弹性。特别说明:实例中的DynamicLoadBean在web环境下运行会出错,需要将类中AbstractApplicationContext改为org.springframework.context.ConfigurableApplicationContext。追问

我的问题已经问了别人解决了,如题,如果配事务的话,一旦有数据出错所有的数据都不会入库,如果没有配的话,只有错误的数据不会入库,正常的还是可以的,虽然你的回答也和题目不对应,不过你的回答也让我受教了,采纳了

参考技术A ID设置了自动增长当然是为NULL埃不知道你这个异常是出现在哪个属性上的。如果你的属性允许为空的话,也要设置追问

文不对题啊,回答的和我问的问题根本对不上

IntelliJ IDEA+springboot+jdbctemplet+easyui+maven+oracle搭建简易开发框架

前言:

  这两天为了巩固easyui的各个控件用法,搭建了一个简易的框架用于开发,大家可以用来参考,如果发现文章中有哪些不正确不合理的地方,也请各位不吝赐教,感激不尽。文章最下面有源码,可以用于参考。整个框架共分三层controller service serviceImpl,并且包括easyui的整合。

 

框架的目录:

  

 

 

一:创建项目

 

打开IntelliJ IDEA软件,点击File-New-Project创建项目

 选中Spring Initializr 点击next

 

 

 

填写项目名 点击finish  注意:刚刚创建好的项目可能会自动引包,这里可以暂时暂停,因为我们后面是要修改pom.xml文件的内容的。

创建之后的项目路径如下:

 

 

 

二:修改配置,部分代码

 

点击File-setting 搜索maven

 修改成功之后,修改下三个文件的内容,之后重新引包 三个文件分别是pom.xml  (这个文件名称是跟你创建项目时候的名称对应生成的)Application.properties

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.14.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->

    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <packaging>war</packaging>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!--&lt;!&ndash;服务器&ndash;&gt;-->
<!--            <scope>provided</scope>-->
            <!--本地-->
                <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0</version>
            <!--<version>12.1.0</version>-->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>1.5.14.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>
pom.xml

 启动类TmApplication

package com.example.tm;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class TmApplication extends SpringBootServletInitializer {

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

    public static void main(String[] args) {
        SpringApplication.run(TmApplication.class, args);
    }
}
TmApplication

Application.properties  注意:数据库连接地址账号和密码都改成自己的就可以了

server.port=8080
server.application.name=demo

spring.datasource.url=jdbc:oracle:thin:@system:1521/orcl
spring.datasource.username=ORCL
spring.datasource.password=orcl
spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver

spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
#热部署文件,页面不产生缓存,及时更新
spring.thymeleaf.cache=false
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
Application.properties

 

 

三:引入easyui

 

在static文件夹下先创建一个文件easyui

 创建之后可以修改下显示的格式,有的人显示是下面这种‘点’连接的,我们改成树的会对结构看的更清楚

 修改成树状结构的方法。

 

从网上下载easyui的包

 

 

 

 

 

 

将解压之后的内容直接复制到static文件夹下

 这里因为test文件我们暂时不用,所以可以先屏蔽掉,不屏蔽启动的时候可能会报错。

 

 

 

四:后台代码

 

参考此目录进行创建

进入页面暂时只需要controller层,这里先将整个框架写好。要注意的是后台类,方法上的注解不要写错。由于暂时没有与后台交互,所以数据库不需要创建表。但是上面配置文件中,还是需要指定数据库的地址账号密码,否则启动项目会报错

package com.example.tm.report.controller;

import com.example.tm.report.service.reportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/report")
public class reportController {

    @Autowired
    private reportService reportservice;

    /**
     * 功能:报表
     * 创建人:tm
     * 时间:2019-12-28
     */
    @GetMapping(value = "/report")
    public String report(){
        return "/report/report";
    }

}
reportController

 reportService

package com.example.tm.report.service;

import org.springframework.stereotype.Service;

@Service("reportService")
public interface reportService {

}
reportService

 ReportServiceImpl

package com.example.tm.report.service.Impl;

import com.example.tm.report.service.reportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service("reportService")
public class ReportServiceImpl implements reportService {

    @Autowired
    private JdbcTemplate jdbcTemplate;



}
ReportServiceImpl

 

 

五:前台代码

 

参考此目录进行创建

注意:需要确认的几个地方

  1. link和script标签指定的easyui相关路径地址名称一定要正确

  2. 在js里如果要写js代码 要写在  /*<![CDATA[*/      /*]]>*/之内

 

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>报表</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" href="../easyui/themes/default/easyui.css"/>
    <link rel="stylesheet" type="text/css" href="../easyui/themes/icon.css"/>
    <script type="text/javascript" src="../easyui/jquery.min.js"></script>
    <script type="text/javascript" src="../easyui/jquery.easyui.min.js"></script>
    <script type="text/javascript" src="../easyui/datagrid-export.js"></script>
    <script type="text/javascript" src="../easyui/locale/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript">
        /*<![CDATA[*/

        /*]]>*/
    </script>
</head>
<body>
<div class="easyui-layout" style="width:100%;height:1000px;" fit="true">
    <div id="tb" style="padding:3px">
        <span>开始时间</span>
        <input type="text" class="easyui-datebox" id="starttime"/>
        <span>结束时间</span>
        <input type="text" class="easyui-datebox" id="endtime"/>
        <span>所在班组</span>
        <select id = "ProjectTree" class="easyui-combotree" url="getClassGroupTree" name="city" style="width:156px;"/>
        <a href="#" class="easyui-linkbutton" iconCls="icon-ok" onclick="doSearch()">查询</a>
        <a href="#" class="easyui-linkbutton" iconCls="icon-ok" onclick="toExcel()">导出</a>
    </div>
</div>
</body>
</html>
report.html

 

 

六:启动项目

 

启动项目分为两种,正常启动和debug模式启动

 如果启动之后如下图所示没有报错,浏览器中直接输入  http://localhost:8080/report/report 就可以了(8080是application.properties文件中配置的,可以自行修改)

显示效果:

 

总结:此框架没有太深的技术,适用于刚刚接触springboot和开始学习搭框架的小伙伴们,接下来的文章里我会依据easyui为基础来完善各种easyui控件的用法,感兴趣的小伙伴可以查看我的博客springboot分类中的内容,如果文章有那些不合理不正确的内容,也请各位大神不吝赐教,小编感激不尽。

 

持续更新!!!

以上是关于java Spring 的JDBCTemplet批量入库数据时如果有一条数据入库不成功,其他的数据还会入库吗的主要内容,如果未能解决你的问题,请参考以下文章

Java/Spring JDBC:批量插入 2 个表:从第 1 批插入获取第 2 个表所需的 FK ID

Spring基础(十三):JDBCTemplate的批操作

如何增加Spring Kafka Consumer每批消费的消息数?

spring整合Quartz定时器

有没有办法跟踪或获取 JPA 在由于 BatchUpdateException 而失败之前完成的批迭代总数?

Spring Cloud之——Config(配置中心)