Spring JDBC-数据连接泄露解读

简介: Spring JDBC-数据连接泄露解读

概述


数据连接泄漏无疑是一个可怕的梦魇。如果存在数据连接泄漏问题,应用程序将因数据连接资源的耗尽而崩溃,甚至还可能引起数据库的崩溃。


Spring DAO 对所有支持的数据访问技术框架都使用模板化技术进行了薄层的封装。只要我们的应用程序都使用 Spring DAO 模板(如 JdbcTemplate、HibernateTemplate 等)进行数据访问,一定不会存在数据连接泄漏的问题 。

因此,我们无需关注数据连接(Connection)及其衍生品(Hibernate 的 Session 等)的获取和释放的操作,模板类已经通过其内部流程替我们完成了,且对开发者是透明的。


但是由于集成第三方产品,整合遗产代码等原因,可能需要直接访问数据源或直接获取数据连接及其衍生品。这时,如果使用不当,就可能在无意中创造出一个魔鬼般的连接泄漏问题。


众所周知,当 Spring 事务方法运行时,就产生一个事务上下文,该上下文在本事务执行线程中针对同一个数据源绑定了一个唯一的数据连接(或其衍生品),所有被该事务上下文传播的方法都共享这个数据连接。这个数据连接从数据源获取及返回给数据源都在 Spring 掌控之中,不会发生问题。如果在需要数据连接时,能够获取这个被 Spring 管控的数据连接,则我们可以放心使用,无需关注连接释放的问题。


那如何获取这些被 Spring 管控的数据连接呢? Spring 提供了两种方法:

  • 其一是使用数据资源获取工具类
  • 其二是对数据源(或其衍生品如 Hibernate SessionFactory)进行代理。


示例:数据连接泄露演示

在具体介绍这些方法之前,让我们先来看一下各种引发数据连接泄漏的场景。


package com.xgj.dao.transaction.dbConnleak;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class JdbcStudentService {
    private Logger logger = Logger.getLogger(JdbcStudentService.class);
    private static final String addStudentSQL = "insert into student(id,name,age,sex) values(student_id_seq.nextval,?,?,?)";
    private JdbcTemplate jdbcTemplate;
    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    public void addStudent(Student student) {
        try {
            // (0)直接从数据源获取连接,后续程序没有显式释放该连接
            Connection connection = jdbcTemplate.getDataSource()
                    .getConnection();
            jdbcTemplate.update(addStudentSQL, student.getName(),
                    student.getAge(), student.getSex());
            Thread.sleep(1000);// (0-1)模拟程序代码的执行时间
            logger.info("addStudent successfully");
        } catch (SQLException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

JdbcStudentService通过 Spring AOP 事务增强的配置,让所有 public 方法都工作在事务环境中。即让addStudent()方法拥有事务功能。在 addStudent() 方法内部,我们在(0)处通过调用 jdbcTemplate.getDataSource().getConnection()显式获取一个连接,这个连接不是 addStudent() 方法事务上下文线程绑定的连接,所以如果我们如果没有手工释放这连接(显式调用 Connection#close() 方法),则这个连接将永久被占用(处于 active 状态),造成连接泄漏!


下面,我们编写模拟运行的代码,查看方法执行对数据连接的实际占用情况

// (1)以异步线程的方式执行JdbcStudentService#addStudent()方法,以模拟多线程的环境
    public static void asynchrLogon(JdbcStudentService userService,
            Student student) {
        StudentServiceRunner runner = new StudentServiceRunner(userService,
                student);
        runner.start();
    }
    private static class StudentServiceRunner extends Thread {
        private JdbcStudentService studentService;
        private Student student;
        public StudentServiceRunner(JdbcStudentService studentService,
                Student student) {
            this.studentService = studentService;
            this.student = student;
        }
        public void run() {
            studentService.addStudent(student);
        }
    }
    // (2) 让主执行线程睡眠一段指定的时间
    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 
     * 
     * @Title: reportConn
     * 
     * @Description: (3)汇报数据源的连接占用情况
     * 
     * @param basicDataSource
     * 
     * @return: void
     */
    public static void reportConn(BasicDataSource basicDataSource) {
        System.out.println("连接数[active:idle]-["
                + basicDataSource.getNumActive() + ":"
                + basicDataSource.getNumIdle() + "]");
    }
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "com/xgj/dao/transaction/dbConnleak/conf_conn_leak.xml");
        JdbcStudentService jdbcStudentService = (JdbcStudentService) ctx
                .getBean("jdbcStudentService");
        BasicDataSource basicDataSource = (BasicDataSource) ctx
                .getBean("dataSource");
        // (4)汇报数据源初始连接占用情况
        JdbcStudentService.reportConn(basicDataSource);
        Student student = new Student();
        student.setAge(20);
        student.setName("LEAK");
        student.setSex("MALE");
        JdbcStudentService.asynchrLogon(jdbcStudentService, student);
        JdbcStudentService.sleep(500);
        // (5)此时线程A正在执行JdbcStudentService#addStudent()方法
        JdbcStudentService.reportConn(basicDataSource);
        JdbcStudentService.sleep(2000);
        // (6)此时线程A所执行的JdbcStudentService#addStudent()方法已经执行完毕
        JdbcStudentService.reportConn(basicDataSource);
        JdbcStudentService.asynchrLogon(jdbcStudentService, student);
        JdbcStudentService.sleep(500);
        // (7)此时线程B正在执行JdbcStudentService#addStudent()方法
        JdbcStudentService.reportConn(basicDataSource);
        JdbcStudentService.sleep(2000);
        // (8)此时线程A和B都已完成JdbcStudentService#addStudent()方法的执行
        JdbcStudentService.reportConn(basicDataSource);
    }

在 JdbcStudentService中添加一个可异步执行 addStudent() 方法的 asynchrLogon() 方法,我们通过异步执行 addStudent() 以及让主线程睡眠的方式模拟多线程环境下的执行场景。在不同的执行点,通过 reportConn() 方法汇报数据源连接的占用情况。


配置文件

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
    <context:component-scan base-package="com.xgj.dao.transaction.dbConnleak" />
    <!-- 使用context命名空间,配置数据库的properties文件 -->
    <context:property-placeholder location="classpath:spring/jdbc.properties" />
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" 
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.url}" 
        p:username="${jdbc.username}" 
        p:password="${jdbc.password}" />
    <!-- 配置Jdbc模板 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />
    <!--事务管理器,通过属性引用数据源 -->
    <bean id="jdbcManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource"/>
    <!-- 通过aop 配置事务增强 -->
    <aop:config  proxy-target-class="true">
        <!-- 切点 -->
        <aop:pointcut  id="serviceJdbcMethod" expression="within(com.xgj.dao.transaction.dbConnleak.JdbcStudentService)"/>
        <!-- 切面 -->
        <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="txAdvice"/>
    </aop:config>
    <!-- 增强,供aop:advisor引用 -->
    <tx:advice id="txAdvice" transaction-manager="jdbcManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
</beans>

保证 BasicDataSource 数据源的配置默认连接为 0,运行程序

2017-09-26 22:38:26,862  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4680937b: startup date [Tue Sep 26 22:38:26 BOT 2017]; root of context hierarchy
2017-09-26 22:38:26,951  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/transaction/dbConnleak/conf_conn_leak.xml]
连接数[active:idle]-[0:0]
连接数[active:idle]-[1:0]
2017-09-26 22:38:29,975  INFO [Thread-1] (JdbcStudentService.java:35) - addStudent successfully
连接数[active:idle]-[1:1]
连接数[active:idle]-[3:0]
2017-09-26 22:38:31,872  INFO [Thread-2] (JdbcStudentService.java:35) - addStudent successfully
连接数[active:idle]-[2:1]


我们通过下表对数据源连接的占用和泄漏情况进行描述


20170927110904868.jpg


可见在执行线程 1 执行完毕后,只释放了一个数据连接,还有一个数据连处于 active 状态,说明泄漏了一个连接。相似的,执行线程 2 执行完毕后,也泄漏了一个连接:原因是直接通过数据源获取连接(jdbcTemplate.getDataSource().getConnection())而没有显式释放造成的。


事务环境下通过DataSourceUtils获取数据连接


Spring 提供了一个能从当前事务上下文中获取绑定的数据连接的工具类- DataSourceUtils。


Spring 强调必须使用 DataSourceUtils 工具类获取数据连接,Spring 的 JdbcTemplate 内部也是通过 DataSourceUtils 来获取连接的。


DataSourceUtils 提供了若干获取和释放数据连接的静态方法


static Connection doGetConnection(DataSource

dataSource):首先尝试从事务上下文中获取连接,失败后再从数据源获取连接;


static Connection getConnection(DataSource dataSource):和doGetConnection 方法的功能一样,实际上,它内部就是调用 doGetConnection 方法获取连接的;


static void doReleaseConnection(Connection con, DataSourcedataSource):释放连接,放回到连接池中;


static void releaseConnection(Connection con, DataSource

dataSource):和 doReleaseConnection 方法的功能一样,实际上,它内部就是调用 doReleaseConnection 方法获取连接的;


来看一下 DataSourceUtils 从数据源获取连接的关键代码:

public abstract class DataSourceUtils {
    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
        //①首先尝试从事务同步管理器中获取数据连接
        ConnectionHolder conHolder = 
            (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder != null && (conHolder.hasConnection() || 
            conHolder.isSynchronizedWithTransaction())) { 
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug(
                    "Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(dataSource.getConnection());
            }
            return conHolder.getConnection();
        }
        //②如果获取不到,则直接从数据源中获取连接
        Connection con = dataSource.getConnection();
        //③如果拥有事务上下文,则将连接绑定到事务上下文中
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            ConnectionHolder holderToUse = conHolder;
            if (holderToUse == null) {
                holderToUse = new ConnectionHolder(con);
            }
            else {holderToUse.setConnection(con);}
            holderToUse.requested();
            TransactionSynchronizationManager.registerSynchronization(
                new ConnectionSynchronization(holderToUse, dataSource));
            holderToUse.setSynchronizedWithTransaction(true);
            if (holderToUse != conHolder) {
                TransactionSynchronizationManager.bindResource(
                dataSource, holderToUse);
            }
        }
        return con;
    }
}

它首先查看当前是否存在事务管理上下文,并尝试从事务管理上下文获取连接,如果获取失败,直接从数据源中获取连接。在获取连接后,如果当前拥有事务上下文,则将连接绑定到事务上下文中。


我们对上面那个有连接泄露的方法进行改造,使用 DataSourceUtils.getConnection() 替换直接从数据源中获取连接的代码:

public void addStudent(Student student) {
        try {
            // (0)直接从数据源获取连接,后续程序没有显式释放该连接
            // Connection connection = jdbcTemplate.getDataSource()
            // .getConnection();
            // 在事务环境下,通过DataSourceUtils获取数据连接
            Connection coon = DataSourceUtils.getConnection(jdbcTemplate
                    .getDataSource());
            jdbcTemplate.update(addStudentSQL, student.getName(),
                    student.getAge(), student.getSex());
            Thread.sleep(1000);// (0-1)模拟程序代码的执行时间
            logger.info("addStudent successfully");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

重新运行日志如下:

2017-09-26 23:19:32,588  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2c686c5e: startup date [Tue Sep 26 23:19:32 BOT 2017]; root of context hierarchy
2017-09-26 23:19:32,719  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/transaction/dbConnleak/conf_conn_leak.xml]
连接数[active:idle]-[0:0]
连接数[active:idle]-[0:0]
2017-09-26 23:19:36,716  INFO [Thread-1] (JdbcStudentService.java:40) - addStudent successfully
连接数[active:idle]-[0:1]
连接数[active:idle]-[1:0]
2017-09-26 23:19:38,273  INFO [Thread-2] (JdbcStudentService.java:40) - addStudent successfully
连接数[active:idle]-[0:1]


我们可以看到已经没有连接泄漏的现象了。一个执行线程在运行 JdbcStudentService#addStudent() 方法时,只占用一个连接,而且方法执行完毕后,该连接马上释放。这说明通过 DataSourceUtils.getConnection() 方法确实获取了方法所在事务上下文绑定的那个连接,而不是像原来那样从数据源中获取一个新的连接。


非事务环境下通过DataSourceUtils获取数据连接也可能造成泄漏


如果 DataSourceUtils 在没有事务上下文的方法中使用 getConnection() 获取连接,依然会造成数据连接泄漏!


我们保持使用DataSourceUtils获取数据源的代码不变,修改下配置文件中的AOP增强,去掉事务增强(如下部分)

<!-- 通过aop 配置事务增强 -->
    <aop:config  proxy-target-class="true">
        <!-- 切点 -->
        <aop:pointcut  id="serviceJdbcMethod" expression="within(com.xgj.dao.transaction.dbConnleak.JdbcStudentService)"/>
        <!-- 切面 -->
        <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="txAdvice"/>
    </aop:config>
    <!-- 增强,供aop:advisor引用 -->
    <tx:advice id="txAdvice" transaction-manager="jdbcManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>


再此运行

2017-09-26 23:23:04,538  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7ba2a618: startup date [Tue Sep 26 23:23:04 BOT 2017]; root of context hierarchy
2017-09-26 23:23:04,655  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/transaction/dbConnleak/conf_conn_leak.xml]
连接数[active:idle]-[0:0]
连接数[active:idle]-[0:0]
2017-09-26 23:23:07,759  INFO [Thread-1] (JdbcStudentService.java:40) - addStudent successfully
连接数[active:idle]-[1:1]
连接数[active:idle]-[2:1]
2017-09-26 23:23:09,504  INFO [Thread-2] (JdbcStudentService.java:40) - addStudent successfully
连接数[active:idle]-[2:1]



有事务上下文时,需要等到整个事务方法(即 addStudent())返回后,事务上下文绑定的连接才释放。但在没有事务上下文时,addStudent() 调用 JdbcTemplate 执行完数据操作后,马上就释放连接。

为了避免这种情况,需要进行如下改造

public void addStudent(Student student) {
        Connection conn = null;
        try {
            // 在非事务环境下,通过DataSourceUtils获取数据连接
            conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
            jdbcTemplate.update(addStudentSQL, student.getName(),
                    student.getAge(), student.getSex());
            Thread.sleep(1000);// (0-1)模拟程序代码的执行时间
            logger.info("addStudent successfully");
            // (1)
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 必须显式使用DataSourceUtils释放连接,否则造成了解泄露
            DataSourceUtils.releaseConnection(conn,
                    jdbcTemplate.getDataSource());
        }
    }

显式调用 DataSourceUtils.releaseConnection() 方法释放获取的连接。特别需要指出的是:一定不能在 (1)处释放连接!因为如果 addStudent() 在获取连接后,(1)处代码前这段代码执行时发生异常,则(1)处释放连接的动作将得不到执行。这将是一个非常具有隐蔽性的连接泄漏的隐患点。


JdbcTemplate 如何做到对连接泄漏的免疫


分析 JdbcTemplate 的代码,我们可以清楚地看到它开放的每个数据操作方法,首先都使用 DataSourceUtils 获取连接,在方法返回之前使用 DataSourceUtils 释放连接。


来看一下 JdbcTemplate 最核心的一个数据操作方法 execute():

public <T> T execute(StatementCallback<T> action) throws DataAccessException {
    //① 首先根据DataSourceUtils获取数据连接
    Connection con = DataSourceUtils.getConnection(getDataSource());
    Statement stmt = null;
    try {
        Connection conToUse = con;
        handleWarnings(stmt);
        return result;
    }
    catch (SQLException ex) {
        JdbcUtils.closeStatement(stmt);
        stmt = null;
        DataSourceUtils.releaseConnection(con, getDataSource());
        con = null;
        throw getExceptionTranslator().translate(
            "StatementCallback", getSql(action), ex);
    }
    finally {
        JdbcUtils.closeStatement(stmt);
        //② 最后根据DataSourceUtils释放数据连接
        DataSourceUtils.releaseConnection(con, getDataSource());
    }
}


在 ① 处通过 DataSourceUtils.getConnection() 获取连接,在 ② 处通过 DataSourceUtils.releaseConnection() 释放连接。


所有 JdbcTemplate 开放的数据访问方法最终都是通过 execute(StatementCallback<T> action)执行数据访问操作的,因此这个方法代表了 JdbcTemplate 数据操作的最终实现方式。


正是因为 JdbcTemplate 严谨的获取连接,释放连接的模式化流程保证了 JdbcTemplate 对数据连接泄漏问题的免疫性。所以,如有可能尽量使用 JdbcTemplate,HibernateTemplate 等这些模板进行数据访问操作,避免直接获取数据连接的操作。


使用 TransactionAwareDataSourceProxy


如果不得已要显式获取数据连接,除了使用 DataSourceUtils 获取事务上下文绑定的连接外,还可以通过 TransactionAwareDataSourceProxy 对数据源进行代理。数据源对象被代理后就具有了事务上下文感知的能力,通过代理数据源的 getConnection() 方法获取的连接和使用 DataSourceUtils.getConnection() 获取连接的效果是一样的。


下面是使用 TransactionAwareDataSourceProxy 对数据源进行代理的配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" 
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.url}" 
        p:username="${jdbc.username}" 
        p:password="${jdbc.password}" />
<!-- ①对数据源进行代理-->
<bean id="dataSourceProxy"
    class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"
    p:targetDataSource-ref="dataSource"/>
<!-- ②直接使用数据源的代理对象-->
<bean id="jdbcTemplate"
    class="org.springframework.jdbc.core.JdbcTemplate"
    p:dataSource-ref="dataSourceProxy"/>
<!-- ③直接使用数据源的代理对象-->
<bean id="jdbcManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    p:dataSource-ref="dataSourceProxy"/>

对数据源进行代理后,我们就可以通过数据源代理对象的 getConnection() 获取事务上下文中绑定的数据连接了。


因此,如果数据源已经进行了 TransactionAwareDataSourceProxy 的代理,而且方法存在事务上下文,那么最开始的代码也不会生产连接泄漏的问题。


其它数据访问技术的等价类


Spring 为每个数据访问技术框架都提供了一个获取事务上下文绑定的数据连接(或其衍生品)的工具类和数据源(或其衍生品)的代理类。


DataSourceUtils 的等价类


数据访问框架 连接获取工具类


image.png

TransactionAwareDataSourceProxy 的等价类


image.png


总结


使用 Spring JDBC 时如果直接获取 Connection,可能会造成连接泄漏。为降低连接泄漏的可能,尽量使用 DataSourceUtils 获取数据连接。也可以对数据源进行代理,以便将其拥有事务上下文的感知能力;


可以将 Spring JDBC 防止连接泄漏的解决方案平滑应用到其它的数据访问技术框架中


示例源码


代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

相关文章
|
6月前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
6843 95
|
6月前
|
NoSQL Java 数据库连接
《深入理解Spring》Spring Data——数据访问的统一抽象与极致简化
Spring Data通过Repository抽象和方法名派生查询,简化数据访问层开发,告别冗余CRUD代码。支持JPA、MongoDB、Redis等多种存储,统一编程模型,提升开发效率与架构灵活性,是Java开发者必备利器。(238字)
|
Java 数据库连接 数据库
【YashanDB知识库】jdbc查询st_geometry类型的数据时抛出YAS-00101 cannot allocate 0 bytes for anlHeapMalloc异常
【YashanDB知识库】jdbc查询st_geometry类型的数据时抛出YAS-00101 cannot allocate 0 bytes for anlHeapMalloc异常
|
Java 数据库连接 数据库
【YashanDB知识库】WAS配置YashanDB JDBC连接
【YashanDB知识库】WAS配置YashanDB JDBC连接
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
916 0
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——使用 fastJson 处理 null
本文介绍如何使用 fastJson 处理 null 值。与 Jackson 不同,fastJson 需要通过继承 `WebMvcConfigurationSupport` 类并覆盖 `configureMessageConverters` 方法来配置 null 值的处理方式。例如,可将 String 类型的 null 转为 &quot;&quot;,Number 类型的 null 转为 0,避免循环引用等。代码示例展示了具体实现步骤,包括引入相关依赖、设置序列化特性及解决中文乱码问题。
627 0
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——Spring Boot 默认对Json的处理
本文介绍了在Spring Boot中返回Json数据的方法及数据封装技巧。通过使用`@RestController`注解,可以轻松实现接口返回Json格式的数据,默认使用的Json解析框架是Jackson。文章详细讲解了如何处理不同数据类型(如类对象、List、Map)的Json转换,并提供了自定义配置以应对null值问题。此外,还对比了Jackson与阿里巴巴FastJson的特点,以及如何在项目中引入和配置FastJson,解决null值转换和中文乱码等问题。
1666 0
|
9月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
792 3
|
9月前
|
SQL XML Java
配置Spring框架以连接SQL Server数据库
最后,需要集成Spring配置到应用中,这通常在 `main`方法或者Spring Boot的应用配置类中通过加载XML配置或使用注解来实现。
676 0
|
Java 数据库连接 定位技术
【YashanDB知识库】如何使用jdbc向YashanDB批量插入gis数据
本文以GIS表为例,介绍通过Java代码向数据库插入POINT类型地理数据的方法。首先创建包含ID和POS字段的GIS表,POS字段为ST_GEOMETRY类型。接着利用Java的PreparedStatement批量插入10条经纬度相同的POINT数据,最后查询结果显示成功插入10条记录,验证了操作的正确性。
224 19