• 概述
  • 使用Spring JDBC
  • 基本的数据操作
    • 更改数据
    • 返回数据库表的自增主键值
    • 批量更改数据
    • 查询数据
    • 使用RowCallbackHandler处理结果集
    • 使用RowMapperT处理结果集
    • RowCallbackHandler和RowMapperT的比较
    • 查询单值数据
    • 调用存储过程3种方式
  • 示例源码

概述

Spring JDBC是Spring所提供的持久层技术,它的主要目的降低JDBC API的使用难度,以一种更直接、更简洁的方式使用JDBC API。

Spring JDBC中,仅仅需要做那些和业务相关的DML操作的事儿而将获取资源、Statement创建、释放资源以及异常处理等繁杂乏味的工作交给Spring JDBC.


使用Spring JDBC

Spring JDBC通过模板回调机制大大降低了使用JDBC的复杂度。

一般情况下,都是在DAO类中使用JdbcTemplate,JdbcTemplate在XML配置文件中后,在DAO中直接注入引用JdbcTemplate即可.

我们看一个配置文件的例子

<?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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.demo"/><!-- 不使用context命名空间,则需要定义Bean<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  <property name="locations" value="classpath:spring/jdbc.properties" />  </bean> --><!-- 使用context命名空间,同上面的Bean等效.在xml文件中配置数据库的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" /></beans>

在Spring配配置那文件中配置DAO一般分为4个步骤

  • 定义DataSource

  • 定义JdbcTemplate

  • 声明一个抽象的Bean,以便所有的DAO复用配置JdbcTemplate属性的配置(使用注解的方式更加方便)

  • 配置具体的DAO(使用注解的方式更加方便)


其中JdbCTemplate有几个属性可以控制底层JDBC API的属性。

  1. queryTimeout 查询数据的最大超时时间,默认为0 ,表示使用底层JDBC驱动程序的默认设置

  2. fetchSize:设置底层的ResultSet每次从数据库返回的行数,该属性对程序的性能影响较大,如果设置过大,因为一次性载入的数据都会放到内存中,所以内存消耗会很大,反之设置的过小,从数据库读取的次数将增大,也会影响性能。 默认为0 ,表示使用底层JDCB驱动程序的默认设置。 Oracle驱动程序的fetchsize的默认值为10

  3. maxRows:设置底层的ResutlSet从数据库返回的最大行数,默认为0 ,表示使用底层JDBC驱动程序默认的设置

  4. ignoreWarnings :是否忽略SQL的告警信息。默认为true,即所有的告警信息都记录到日志中,如果设置为false,则JdbcTemplate将抛出SQLWarningException


基本的数据操作

数据库的增删改查(CRUD)及存储过程调用是最常见的数据库操作,JdbcTemplate提供了众多的方法,通过JdbcTemplate可以用简单的方法完成这些数据操作。


下面我们以示例来实际演示下这些操作

更改数据

JdbcTemplate提供了若干个update方法,允许对数据表记录记录进行更改和删除操作。

首先我们定义一个抽象的DAO基类, BaseDao。 其中暂时我们只封装了注入JDBC的操作(扩展的话可以将分页等通用的功能抽取到BaseDao中等)

package com.xgj.dao.basicOperation;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;public abstract class BaseDao {public JdbcTemplate jdbcTemplate;// 注入JdbcTemplate实例@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}
}

然后我们编写DAO层的代码,简单旗舰,直接将DAO定义成了类。一般来讲将DAO层编写成接口更合适。

package com.xgj.dao.basicOperation.insertUpdateAndDelete;import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.stereotype.Repository;import com.xgj.dao.demo.BaseDao;/*** * * @ClassName: ArtisanDao* * @Description: @Repository标注的DAO* * @author: Mr.Yang* * @date: 2017年9月18日 下午4:19:06*/@Repository
public class ArtisanDao extends BaseDao {private static final String sql = "insert into artisan_user(user_name,password) values(?,?)";/*** * * @Title: addSingleArtisan* * @Description: 增加一个Artisan* * @param artisan* * @return: void*/public void addSingleArtisan(Artisan artisan) {jdbcTemplate.update(sql, artisan.getUserName(), artisan.getPassword());System.out.println("insert successfully");}}

由于JdbcTemplate在内部通过PreparedStatement执行SQL语句,所以可以绑定参数的SQL语句,每个“?”占位符可以接受一个参数。

尽量使用可绑定参数的SQL语句,以便数据库可以复用SQL的执行计划,提高数据库的执行效率。 此外,应该在DAO使用类级别的静态常量(final static)定义SQL字符串,不应该在方法内部声明SQL字符串变量,以提高JVM的内存使用效率。

在通过public int update(String sql, Object... args) throws DataAccessException 方法为SQL语句的占位符绑定参数时,并没有显示的指定对应字段的数据类型,此时,Spring直接让PreparedStatement根据参数的类型进行“猜测”。 有一种更好的做法是使用public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException显示指定每个占位符所对英的字段数据类型,这样就可以保证类型安全,当参数值为null时,这种形式提供了更好的支持。

以下代码仅为演示

// 使用该类中的常量属性定义参数类型
import java.sql.Type ....jdbcTemplate,update(sql, new Object[]{..} ,new int[]{Types.VARCHAR2...});....

配置文件如下(以下的几个操作都加载这个配置文件)

<?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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.basicOperation" /><!-- 不使用context命名空间,则需要定义Bean <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:spring/jdbc.properties" /> </bean> --><!-- 使用context命名空间,同上面的Bean等效.在xml文件中配置数据库的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" /></beans>

测试类

package com.xgj.dao.basicOperation.insertUpdateAndDelete;import java.util.ArrayList;
import java.util.List;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class ArtisanDaoTest {public static void main(String[] args) {// 启动Spring 容器ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/basicOperation/basicOperation.xml");Artisan artisan = ctx.getBean("artisan", Artisan.class);artisan.setUserName("Artisan");artisan.setPassword("987654");ArtisanDao artisanDao = ctx.getBean("artisanDao", ArtisanDao.class);// 调用目标方法artisanDao.addSingleArtisan(artisan);
}

除了上述两个update方法外,JdbcTemplate还提供了以下几个功能相似的重载方法


  • public int update(final String sql) 为不带占位符的SQL语句提供的便利方法

  • public int update(String sql, PreparedStatementSetter pss) PreparedStatementSetter 是一个回调接口,它定义了一个setValues(PreparedStatement ps)接口方法 ,如下所示

public void addStudent(Student student){jdbcTemplate.update(sql,new PreparedStatementSetter (){public void setValues(PreparedStatement  ps) throws SQLException{ps.setString(1,student.getName());ps.setString(2,student,getSex());}
});}

PreparedStatement绑定参数时,参数索引从1开始,而非0开始。 第一个参数索引为1,第二个参数索引为2,依次类推。

当然了,还有其他方法 ,需要指出的是,在实际用用中,应该优先考虑不带回调接口的JdbcTemplate方法。没有必要使用那些带有回调接口的方法,因为Spring会在内部自动创建这些回调实例。


返回数据库表的自增主键值

举个例子 ORACLE数据库

com.xgj.dao.transaction.annotationTrans.dao.impl.StudentDaoImpl.java

@Overridepublic void addStudent(final Student student) {// 这里采用和addTeacher不同的方式,输出插入数据库的主键IDKeyHolder keyHolder = new GeneratedKeyHolder();PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() {@Overridepublic PreparedStatement createPreparedStatement(Connection con)throws SQLException {PreparedStatement ps = con.prepareStatement(addTeacherSQL,new String[] { "id" });ps.setString(1, student.getName());ps.setInt(2, student.getAge());ps.setString(3, student.getSex());return ps;}};jdbcTemplate.update(preparedStatementCreator, keyHolder);System.out.println("获取到的插入数据库的ID:" + keyHolder.getKey().longValue());}

在实际开发中,我们并不太建议使用表自增键,因为这种方式会让开发变得更加复杂且降低程序的移植性,在应用层中创建主键才是主流的方式,可以使用UUID或者通过一个编码引擎获取主键值。


批量更改数据

如果需要一次性插入或者更新多条记录,当然可以简单的通过多次调用update()方法完成任务,但是这不是最好的实现方案。 更好的选择是使用JDBCTemplate批量数据更改的方法。一般情况下,后者拥有更好的性能,因为更新的数据将被批量发送到数据库中,它减少了对数据库访问的次数。

我们解读下下面两个方法:

  • public int[] batchUpdate(String[] sql)
    多条SQL语句组成一个数组,注意此处的sql语句不能带参数,该方法以批量方式执行这些SQL语句。Spring在内部使用JDBC提供的批量更新API完成操作,如果底层的JDBC Driver不支持批量更新操作,Spring将采用逐条更新的方式模拟批量更新。

  • int[] batchUpdate(String sql,BatchPreparedStatementSetter pss)使用本方法对于同一结构的带参SQL语句多次进行数据更新操作。通过BatchPreparedStatementSetter回调接口进行批量参数的绑定工作。

BatchPreparedStatementSetter定义了两个方法:

  • int getBatchSize():指定本批次的大小
  • void setValues(PreparedStatement ps,int i):为给定的PreparedStatement设置参数
/*** * * @Title: addBatchArtisan* * @Description: 批量更新* * @param artisanList* * @return: void*/public void addBatchArtisan(final List<Artisan> artisanList) {jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {@Overridepublic void setValues(PreparedStatement ps, int index)throws SQLException {Artisan artisan = artisanList.get(index);ps.setString(1, artisan.getUserName());ps.setString(2, artisan.getPassword());}// 指定该批的记录数@Overridepublic int getBatchSize() {return artisanList.size();}});System.out.println("batch insert successfully");}

需要注意的是BatchPreparedStatementSetter是一次性地批量提交数据,而不会分批提交,getBatchSize()是整批的大小。所以,如果希望将一个List中的数据通过BatchPreparedStatementSetter批量更新到数据库中,getBatchSize()就应该设置为List的大小。

如果List非常大,希望分多次批量提交,则可分段读取这个大List并暂存到一个小的List中,再将这个小的List通过BatchPreparedStatemetSetter批量保存到数据库中。


查询数据

在Spring JDBC中,仅需要指定SQL查询语句并定义好如何从结果集中返回数据就可以了。

使用RowCallbackHandler处理结果集

Spring提供了org.springframework.jdbc.core.RowCallbackHandler回调接口,通过该接口可以定义如何从结果集中获取数据. RowCaIlbackHandler接口很简单,仅有一 个方法void processRow(ResultSet rs) throws SQLException
Spring会遍历结果集, 对结果集中的每一行调用RowCallbackHandler回调接口处理数据。所以用户无 须 调用ResultSet的next()方法,而只需要定义好如何获取结果行数据的逻辑就可以了。

我们来看个示例

package com.xgj.dao.basicOperation.retrieve_select;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;import com.xgj.dao.demo.BaseDao;/*** * * @ClassName: ArtisanRDao* * @Description: @Repository 标注的DAO* * @author: Mr.Yang* * @date: 2017年9月18日 下午6:14:24*/@Repository
public class ArtisanRDao extends BaseDao {private static final String selectArtisanUserSql = "select user_name ,password from artisan_user where user_id = ? ";private static final String selectArtisanUsersSql = "select user_name ,password from artisan_user where user_id between ? and ? ";/*** * * @Title: selectArtisanById* * @Description: 取一条数据* * @param artisanId* @return* * @return: Artisan*/public Artisan selectArtisanById(int artisanId) {final Artisan artisan = new Artisan();// (1)将结果集中的数据抽取到artisan对象中jdbcTemplate.query(selectArtisanUserSql, new Object[] { artisanId },new RowCallbackHandler() {@Overridepublic void processRow(ResultSet rs) throws SQLException {artisan.setUserName(rs.getString("user_name"));artisan.setPassword(rs.getString("password"));}});return artisan;}
}

如果需要获取多条记录,依旧可以使用RowCallbackHandler完成任务,只需要稍微调整一下结果集的处理逻辑就可以了。 代码如下

/*** * * @Title: selectArtisansByIds* * @Description: 使用RowCallbackHandler获取多条记录* * @param beginId* @param toId* @return* * @return: List<Artisan>*/public List<Artisan> selectArtisansByIds(int beginId, int toId) {final List<Artisan> artisanList = new ArrayList<Artisan>();jdbcTemplate.query(selectArtisanUsersSql,new Object[] { beginId, toId }, new RowCallbackHandler() {@Overridepublic void processRow(ResultSet rs) throws SQLException {Artisan artisan = new Artisan();artisan.setUserName(rs.getString("user_name"));artisan.setPassword(rs.getString("password"));artisanList.add(artisan);}});return artisanList;}

当结果集中没有数据时,并不会抛出异常。只是此时RowCallbackHandle:回调接口 中定义的处理逻辑没有得到调用罢了。


使用RowMapper<T>处理结果集

Spring还提供了一个和RowCallbackHandler功能类似的RowMapper<T>接口,它也可以使用RowMapper定义结果集映射逻辑,在结果集为多行记录时,该接口更 容易使用。RowMapper<T>也只有一个接口方法:

T mapRow(ResultSet rs, int rowNum)

看下示例

    /*** * * @Title: selectArtisansByIdsWithRowMapper* * @Description: 使用RowMapper获取多行结果集* * @param beginId* @param toId* @return* * @return: List<Artisan>*/public List<Artisan> selectArtisansByIdsWithRowMapper(int beginId, int toId) {return jdbcTemplate.query(selectArtisanUsersSql, new Object[] {beginId, toId }, new RowMapper<Artisan>() {@Overridepublic Artisan mapRow(ResultSet rs, int rowNum) throws SQLException {Artisan artisan = new Artisan();artisan.setUserName(rs.getString("user_name"));artisan.setPassword(rs.getString("password"));return artisan;}});}

RowCallbackHandler和RowMapper<T>的比较

从功能上讲,RowCallbackHandler和RowMapper没有太大的区别,它们都是用于定义结果集行的读取逻辑,将ResultSet中的数据映射到对象或者List中 。

RowCallbackHandler接口实现类可以是有状态的,而RowMapper的实现类应该是无状态的。如果RowCallbackHandler实现类是有状态的,用户就不能在多个地方复用,只有无状态的实例都能在不同的地方复用。

比如,Spring有一个RowCallbackHandler的实现类是RowCountCallbackHandler,可以计算结果集行数:

RowCountCallbackHandler countCallback = new RowCountCallbackHandler();
jdbcTemplate.query("select * from user", countCallback);
int rowCount = countCallback.getRowCount();

可见RowCountCallbackHandler包含了一个记录结果集行数的状态,在多线程的环境中,如果没有进行特殊的处理,就不能在多个地方复用countCallback实例。

Spring也提供了几个RowMapper实现类,如ColumnMapRowMapper和SingleColumnRowMapper。

  • ColumnMapRowMapper将结果集中的每一行映射为一个
  • MapSingleColumnRowMapper将结果集中的某一列映射为一个Object。它们都只是定义了映射逻辑,而没有保持状态。

我们知道,通过JDBC查询返回一个ResultSet结果集时,JDBC并不会一次性将所有匹配的数据都加载到JVM中,而是只返回同一批次的数据(由JDBC驱动程序决定,如Oracle的JDBC驱动程序默认返回10行数据),当通过ResultSet#next()游标流动结果集超过数据范围时,JDBC再获取一批数据。这样以一种“批量化+串行化”的处理方式避免大结果集处理时JVM内存的过大开销。

当处理大结果集数据时,如果使用RowMapper,则虽然获取数据的过程是串行化的,但是结果集中的所有数据最终都会映射并汇总成一个List对象,占用大量的JVM内存,甚至可以直接引发OutOfMemoryException异常。这里应该使用RowCallbackHandler接口,在processRow接口方法内部处理结果集数据。

当使用RowCallbackHandler接口时,如果结果集中没有数据,并不会抛出异常,只是此时RowCallbackHandler回调接口中定义的处理逻辑没有得到调用罢了。


查询单值数据

Both queryForInt() and queryForLong() are deprecated since version 3.2.2 . To fix it, replace the code with queryForObject(String, Class).

package com.xgj.dao.basicOperation.getSingleValue;import org.springframework.stereotype.Repository;import com.xgj.dao.basicOperation.BaseDao;/*** * * @ClassName: GetCountOfArtisanDao* * @Description: @Repository 标注的DAO* * @author: Mr.Yang* * @date: 2017年9月19日 下午12:05:08*/@Repository
public class GetCountOfArtisanDao extends BaseDao {private final static String COUNTSQL = "select count(1) from artisan_user where user_name = ? ";/*** * * @Title: getCount* * @Description: Both queryForInt() and queryForLong() are deprecated since*               version 3.2.2 (correct me if mistake). To fix it, replace*               the code with queryForObject(String, Class).* *               https://www.mkyong.com/spring/jdbctemplate-queryforint-is-*               deprecated/* * @return* * @return: int*/public boolean getCount(String userName) {boolean isExist = false;int count = jdbcTemplate.queryForObject(COUNTSQL,new Object[] { userName }, Integer.class);if (count > 0) {isExist = true;} else {isExist = false;}return isExist;}
}

调用存储过程(3种方式)

CallProcDemo

package com.xgj.dao.basicOperation.callProc;import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.stereotype.Repository;import com.xgj.dao.demo.BaseDao;@Repository
public class CallProcDemo extends BaseDao {// (1) 调用存过的语句private static final String PROCSQL_STRING = "call PROC_artisan_oper(?,?)";public int getUserCount(final String userName) {int num = jdbcTemplate.execute(PROCSQL_STRING,new CallableStatementCallback<Integer>() {@Overridepublic Integer doInCallableStatement(CallableStatement cs)throws SQLException, DataAccessException {// (2)绑定入参cs.setString(1, userName);// (3)注册输出参数cs.registerOutParameter(2, Types.INTEGER);// 执行cs.execute();return cs.getInt(2);}});System.out.println("num:" + num);return num;}
}

CallProcDemoWithSimpleJdbcCall

package com.xgj.dao.basicOperation.callProc;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;@Repository
public class CallProcDemoWithSimpleJdbcCall {private JdbcTemplate jdbcTemplate;@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public void getUserCount(int userId) {SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("PROC_artisan_selectInfo_withId");// 如果调用function 则为withFunctionName(functionName)// 注册入参 必须和存过的入参保持一致 不区分大小写SqlParameterSource in = new MapSqlParameterSource().addValue("p_user_id", userId);// 获取返回结果Map<String, Object> outMap = jdbcCall.execute(in);for (Map.Entry<String, Object> entry : outMap.entrySet()) {System.out.println("key=" + entry.getKey() + ",value="+ entry.getValue());}String userName = (String) outMap.get("O_USERNAME");String password = (String) outMap.get("O_PASSWORD");System.out.println("userName:" + userName + " ,password=" + password);}
}

CallProcDemoWithCallableStatementCreator

package com.xgj.dao.basicOperation.callProc;import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.CallableStatementCreatorFactory;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.stereotype.Repository;import com.xgj.dao.demo.BaseDao;@Repository
public class CallProcDemoWithCallableStatementCreator extends BaseDao {private static final String PROCSQL_STRING = "call PROC_artisan_selectInfo_withId(?,?,?)";@SuppressWarnings({ "rawtypes", "unchecked" })public void printUserInfo(int userId) {// 使用CallableStatementCreatorFactory 创建 CallableStatementCreatorCallableStatementCreatorFactory factory = new CallableStatementCreatorFactory(PROCSQL_STRING);// 设置入参factory.addParameter(new SqlParameter("p_user_id", Types.INTEGER));// 设置出参factory.addParameter(new SqlOutParameter("o_username", Types.VARCHAR));factory.addParameter(new SqlOutParameter("o_password", Types.VARCHAR));Map<String, Integer> paramMap = new HashMap<String, Integer>();paramMap.put("p_user_id", userId);CallableStatementCreator csc = factory.newCallableStatementCreator(paramMap);String userInfo = jdbcTemplate.execute(csc,new CallableStatementCallback() {@Overridepublic Object doInCallableStatement(CallableStatement cs)throws SQLException, DataAccessException {// 执行cs.execute();// 获取返回结果String userName = cs.getString(2);String password = cs.getString(3);String returnInfo = userName + "|" + password;return returnInfo;}});System.out.println("UserInfo:" + userInfo);}
}

示例源码

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

Spring JDBC-使用Spring JDBC访问数据库相关推荐

  1. Spring Boot中使用JdbcTemplate访问数据库

    本文介绍在Spring Boot基础下配置数据源和通过JdbcTemplate编写数据访问的示例. 数据源配置 在我们访问数据库的时候,需要先配置一个数据源,下面分别介绍一下几种不同的数据库配置方式. ...

  2. 使用程序设计语言访问SQL:JDBC、从Python访问数据库、ODBC、嵌入式SQL

    SQL 提供了一种强大的声明式查询语言.用 SQL 编写查询通常比用通用程序设计语言同样的查询进行编码要简单得多.然而,基于至少两种原因数据库程序员必须能够访问通用程序设计语言: 1.因为 SQL 并 ...

  3. Spring实战6-利用Spring和JDBC访问数据库

    主要内容 定义Spring的数据访问支持 配置数据库资源 使用Spring提供的JDBC模板 写在前面:经过上一篇文章的学习,我们掌握了如何写web应用的控制器层,不过由于只定义了SpitterRep ...

  4. JDBC概述(JDBC是什么,主要作用,驱动类型等)

    1. 概述: JDBC是一种可用于执行SQL语句的JAVA API,是链接数据库和JAVA应用程序的纽带 2. 主要任务: JDBC技术主要是完成以下几个任务: 与数据库建立一个链接 向数据库发送SQ ...

  5. Java程序员从笨鸟到菜鸟之(七十八)细谈Spring(七)spring之JDBC访问数据库及配置详解

    利用spring访问数据库是我们ssh程序中必不可少的步骤,在没有hibernate之前,我们一般都用jdbc访问数据库,所以用jdbc访问数据库必不可少的要进行一些配置,spring中为我们提供了访 ...

  6. Java工作笔记-Spring Boot + Jdbc + dm7Driver访问数据库(Spring Boot连接达梦数据库)

    目录 基本概念 代码与实例 基本概念 Jdbc有4种方式访问数据库,下面给出达梦访问数据库的方式. 安装好达梦库后,就会有驱动,在此不再说明怎么去安装! 本次使用Java7的环境,所以使用的Jar包为 ...

  7. Spring JDBC 访问数据库

    Spring JDBC是Spring所提供的持久层技术,它以一种更直接.更简单的方式使用JDBC API.在Spring JDBC里,用户仅需要做那些必不可杀的事儿,而将资源获取.Statement创 ...

  8. spring(10)通过spring 和 JDBC征服数据库

    [0]README 1)本文部分文字描述转自:"Spring In Action(中/英文版)",旨在review  "spring(10)通过spring 和 JDBC ...

  9. 使用Spring JDBC框架连接并操作数据库

    在前一篇博文JAVA通过JDBC连接并操作MySQL数据库中,我们知道如何通过JDBC连接并操作数据库,但是请看程序,整个程序连接数据库和关闭数据库占了很大一部分代码量,而且每次我们执行一下数据库操作 ...

最新文章

  1. cli vue 卸载,vue Cli 环境删除与重装教程 - 版本文档
  2. RESTEasy教程第3部分:异常处理
  3. POJ 1321 棋盘问题(回溯)
  4. python keyerror_盘点Python 初学者最容易犯的10大错误!你中招了吗?
  5. 加码 2000 亿新基建还不够,阿里云再放话:今年招 5000 人!
  6. 解决margin塌陷的问题_剖析一些经典的CSS布局问题,为前端开发+面试保驾护航...
  7. 51nod 1378 夹克老爷的愤怒(树型dp+贪心)
  8. python之路 -- 并发编程之进程
  9. 大数据挑战与NoSQL数据库技术pdf
  10. 转载:数据库应用开发工具Toad使用笔记
  11. VMware16阿里云盘
  12. 15个android框架,Android常用的15个框架总结
  13. 「第二部:容器和微服务架构」(2) 容器化单体应用
  14. 【亲测有效】解决 Ubuntu 虚拟机无法共享文件夹的问题
  15. 华为手机自带浏览器无法下载 iis 网站 apk 问题解决方案(和SSL有关)
  16. 2D 游戏工具系列:unity自带Tilemap和地图编辑器Tiled的基本使用以及Super Tiled2Unity如何导入tmx到Unity中(2)
  17. nginx uwsgi django部署
  18. tf.nn.xw_plus_b()
  19. 数据库存储图片解决方案
  20. 怎样循序渐进、有效地学习JavaScript?

热门文章

  1. python交互式程序设计导论第二周_沧州学堂云Python 交互式程序设计导论搜题公众号...
  2. 计算机视觉基础:图像处理(上)
  3. MATLAB应用实战系列(七十七)-基于长时间序列栅格数据的MK检验
  4. 深度学习核心技术精讲100篇(七十九)-深度学习应用实战案例:携程金融自动化迭代反欺诈模型体系
  5. Java面试题目解析-如何解决Java性能问题
  6. tableau必知必会之学做常用的倾斜图(slopegraph)
  7. 关于MATLAB FFT频谱泄露和加窗
  8. python function函数_Python34-06-函数(function)
  9. 数据分析系列:完善统计图(matplotlib)
  10. win11+AMD的cpu+3060GPU电脑安装 tensorflow-GPU+cuda11+cudnn