spring的jdbcTemplate的实现

一个简单的jdbc的例子:

#Banzu.class

package com.springframework.sample.jdbc.pojo;

//仅仅是作为简单的java对象(pojo),属性值,构造方法,setget方法

public classBanzu {

   private StringbanZu;

   private StringfuZeRen;

   private StringchengYuan;

   public Banzu(){}

   public Banzu(StringbanZu,String fuZeRen,StringchengYuan){

      this.banZu =banZu;

      this.fuZeRen =fuZeRen;

      this.chengYuan =chengYuan;

   }

   public String getBanZu() {

      return banZu;

   }

   public void setBanZu(String banZu) {

      this.banZu =banZu;

   }

   public String getFuZeRen() {

      return fuZeRen;

   }

   public void setFuZeRen(String fuZeRen) {

      this.fuZeRen =fuZeRen;

   }

   public String getChengYuan() {

      return chengYuan;

   }

   public void setChengYuan(String chengYuan) {

      this.chengYuan =chengYuan;

   }

  

}

 

 

#BanzuRowMapper.class implements RowMapper()

//主要实现Rowmapper接口,主要是对于返回ResultSet集合进行封装得到相应的List<Banzu>集合,这是JdbcTemplate中query(stringsql ,RowMapper)的一个参数,实现对于对象封装转换

package com.springframework.sample.jdbc.pojo;

 

import java.sql.ResultSet;

import java.sql.SQLException;

 

import org.springframework.jdbc.core.RowMapper;

 

public class BanzuRowMapper implementsRowMapper<Banzu> {

 

   @Override

   publicBanzu mapRow(ResultSet rs, int rowNum) throws SQLException {

      //TODO Auto-generated method stub

      Banzubanzu = newBanzu(rs.getString("BANZU_NAME"),rs.getString("FUZEREN"),rs.getString("BANZUCHENGYUAN"));

      returnbanzu;

   }

 

}

其中注释写到,RowMapper接口的实现的方法,只要将Resultset中每一row对应到一个对象即可。

#DaoInterface:interface ,提供抽象保存和查询的功能

package com.springframework.sample.jdbc.dao;

 

import java.util.List;

 

import com.springframework.sample.jdbc.pojo.Banzu;

 

public interfaceDaoInterface {

   public void saveBanzu(Banzu banzu);

   public List<Banzu>selectBanzu(Stringsql);

}

#DaoServiceImpl:class ,implements DaoInterface

//利用JdbcTemplate,同时使用basicDataSource注入,进行操作DataBase

package com.springframework.sample.jdbc.dao;

import java.util.ArrayList;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.springframework.sample.jdbc.pojo.Banzu;

import com.springframework.sample.jdbc.pojo.BanzuRowMapper;

 

public classDaoServiceImpl implements DaoInterface {

   private JdbcTemplatejdbcTemplate;

   private DataSourcedataSource;

  

   public JdbcTemplategetJdbcTemplate() {

      return jdbcTemplate;

   }

 

   public voidsetJdbcTemplate(JdbcTemplate jdbcTemplate) {

      this.jdbcTemplate =jdbcTemplate;

   }

   public voidsetDataSource(DataSource dataSource){

      this.jdbcTemplate =new JdbcTemplate(dataSource);

   }

 

   @Override

   public void saveBanzu(Banzu banzu) {

      // TODO Auto-generated method stub

      Stringsql= "insert into banzu(BANZU_NAME,FUZEREN,BANZUCHENGYUAN) VALUES (?,?,?)";

      if(banzu!=null)

      {

         this.jdbcTemplate.update(sql,new Object[]{banzu.getBanZu(),banzu.getFuZeRen(),banzu.getChengYuan()});

      }

   }

 

   @Override

   public List<Banzu>selectBanzu(Stringsql){

      // TODO Auto-generated method stub

      List<Banzu> list  = new ArrayList<Banzu>();

      if(sql.isEmpty())

         sql = "select * from banzu";

      list = this.jdbcTemplate.query(sql,new BanzuRowMapper());

      return list;

   }

 

}

这里有个疑问,虽然是可以注入,但是jdbcTemplate是是在哪里注入的那?这里虽然是将dataSource进行了set注入,同时也new了一个JdbcTemplate的对象,但是它应该先调用jdbcTemplate及其的注入,但是实际上我们并没有看到?难道是加载spring-config.xml其中dataSource初始化了它,进行set它的时候,创建的,但是需要jdbcTemplate时,确实是没有找到相应的注入,难道原因就是这个new,还是spring框架中其他地方有注入?

#ServiceSample1:class service logical ,mainmethod to run

package com.springframework.sample.jdbc.service;

 

import java.util.List;

importorg.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

importcom.springframework.sample.jdbc.dao.DaoInterface;

importcom.springframework.sample.jdbc.pojo.Banzu;

//执行添加表的数据和查询表中数据,并显示出来

public class ServiceSample1 {

   privateDaoInterface daoIntBanzu;

   privateBanzu banZu;

   publicDaoInterface getDaoIntBanzu() {

      returndaoIntBanzu;

   }

   publicvoid setDaoIntBanzu(DaoInterface daoIntBanzu) {

      this.daoIntBanzu= daoIntBanzu;

   }

   publicBanzu getBanZu() {

      returnbanZu;

   }

   publicvoid setBanZu(Banzu banZu) {

      this.banZu= banZu;

   }

   publicvoid createBanzu(Banzu banzu){

      daoIntBanzu.saveBanzu(banzu);

   }

   publicList<Banzu> getBanzu(String sql){

      returndaoIntBanzu.selectBanzu(sql);

   }

   public  static void main(String []args){

   //利用applicationcontext接口获得相应配置文件中的信息

      ApplicationContextcontext = new ClassPathXmlApplicationContext("spring-config.xml");

//初始化一个新的班组,然后加入数据库

      Banzubanzu = new Banzu();

      banzu.setBanZu("ban"+System.currentTimeMillis());

      banzu.setChengYuan("du1,zhao2,long3");

      banzu.setFuZeRen("dudu");

   //通过getBean获取相应注入对象(springIOC容器管理初始化和销毁)

      ServiceSample1serv =(ServiceSample1) context.getBean("service1");

      serv.createBanzu(banzu);

      List<Banzu>list = serv.getBanzu("select * from banzu");

      for(inti=0;i<list.size();i++){

         Banzutemp_banzu = list.get(i);

         System.out.println("banzuname:" +temp_banzu.getBanZu()+",fuzeren:"+temp_banzu.getFuZeRen()+",chengyuan:"+temp_banzu.getChengYuan());

        

      }

   }

}

#spring-config.xml 放置在src目录下即可

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans" 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop" 

    xmlns:tx="http://www.springframework.org/schema/tx" 

    xmlns:context="http://www.springframework.org/schema/context"  

    xsi:schemaLocation=

        http://www.springframework.org/schema/context  

        http://www.springframework.org/schema/context/spring-context-3.0.xsd   

    http://www.springframework.org/schema/beans  

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 

    http://www.springframework.org/schema/tx  

    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  

    http://www.springframework.org/schema/aop  

    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 

    

    <beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

    

      <propertyname="driverClassName" value ="oracle.jdbc.driver.OracleDriver"/>

      <propertyname="url"value ="jdbc:oracle:thin:@localhost:1521:orcl"/>

      <propertyname= "username"value ="scott" />

      <propertyname= "password"value ="adu636"/>

    </bean>

    

    <beanid= "jdbcTemplate"class ="com.springframework.sample.jdbc.dao.DaoServiceImpl">

      <propertyname="dataSource"ref ="dataSource"/>

    </bean>

   

    <beanid="service1"class="com.springframework.sample.jdbc.service.ServiceSample1">

       <propertyname="daoIntBanzu"ref ="jdbcTemplate"/>

   </bean>

    </beans>

//这是使用的oracle10g,数据库驱动则是classes12.jar,ojdbc14.jar,

//spring 3.2.4把其所有的jar包都加入了其中,spring还需要日志记录commons-logging.jar,另外还需要commons-dbcp.jar(是用来BasicDataSource),及其BasicDataSource配套的commons-pool.jar,(数据库连接池)

//同时这里使用的是JDK7的版本,本身系统为win8JDK也是64bit的。

 

其他题的过程:ServiceSample1:main()方法被spring-config.xml中配置完成初始化,----》其中需要依赖注入相应的DaoServiceImpl(daoIntBanzu:DaoInterface)----》而DaoServiceImpl则需要注入相应的dataSource:BasicDataSource,完成数据库相应访问的配置,并利用jdbcTemplate中的数据库操作方法读写数据库。----》一系列的注入完成后,开始添加Banzu和查询该表中对象数据。-----

这里的一个新的知识点就是使用了RowMapper接口,实现了该接口其中的方法。

一般的jdbc操作时:

   Class.forName(“加载相应的驱动”);

   Connectioncon = DriverManager.getConnection(url,username,password);

   Statementstat = con.createStatement();

   ResultSetrs = stat.executeQuery(sql);

   while(rs.next())

   {

      Stringname = rs.getString(“name”);…

}

If(rs!=null)

 {

    rs.close();

rs = null;

}

If(stat!=null){

   Stat.close();

   Stat = null;

}

If(con!=null){

Con.close();

Con = null;

}

这里没有加入异常。

8.1这里我们分析springjdbc中的实现

   通过前面的例子,同时结合JdbcTemplate的定义中我们可以看出

JDBCTEMPLATEspringjdbc core包中的核心类,它简化了jdbc的使用,同时可以避免一些普通的错误。它执行jdbc的工作流,留给应用程序输入sql参数,同时将运行结果result提取作为输出。jdbcTemplate可以执行sql查询或者update,初始化resultset上面的迭代器,并且捕获jdbcexception.

   使用该类需要实现回调接口,进行相关清晰的定义。preparedStatementCreator callbackinterface ,通过connection创建一个PreparedStatement。而ResultSetExtractor接口会从resultset中提出相关数据。可以参考实现PreparedStatementSetterRowMapper接口,前者参数化sql语句预处理防止sql注入,后者RowMapper的接口可以实现方法获得ResultSet中每一行row数据转化为相应的对象实例。

   JdbcTemplate的实例化,可以

   JdbcTemplate  jdbcTemplate = new JdbcTemplate(dataSource);

也就是说可以自己设置相应的数据源,比如BasicDataSource,C3P0等。然后再xml文件中配置一下,然后有spring进行解析注入。

   jdbcTemplate类实现了JdbcAccessor抽象类,实现了JdbcOperation接口,jdbcaccessor类主要是完成了对于dataSourceset方法,延迟加载设置默认为truesql异常转换,logger记录debug级的信息。

Jdbcoperator接口定义的类无外乎queryupdate等方法,主要是针对于statementpreparedstatementcallbackstatement类别的各种queryupdate方法。

  

如果是常亮的话,可以设置为:

Private static final type  CAPS_NAME = “”;//大写名称

Static final type CAPS_NAME = “”; //接口中常亮

 

Jdbctemplate中重要的方法介绍:

(1)  设置数据源的构造方法

Public JdbcTemplate(DataSource dataSource){

         setDataSource(dataSource);

    afterPropertiesSet();//所有参数设置后执行

}

2)通过阅读spring源码,可以了解到一半的比如处理每个业务的如之前的getBean(),方法一半都是处理之前做一些准备,真正的处理则是类似于doGetBean()这样的方法处理的。如下是jdbcTemplate对于connection的处理,利用了DataSourceUtils工具类进行处理

public <T> T execute(ConnectionCallback<T>action)throwsDataAccessException {

      Assert.notNull(action,"Callback object must not be null");

      //(1)DataSourceUtils工具类通过数据源获得connection

      Connectioncon= DataSourceUtils.getConnection(getDataSource());

      try {

         ConnectionconToUse= con;

         if (this.nativeJdbcExtractor!=null){

      //2抽取本地jdbc连接,可以转换成oracle等其他的连接

            conToUse = this.nativeJdbcExtractor.getNativeConnection(con);

         }

         else {

            //3创建连接代理, also preparingreturned Statements.

            conToUse =createConnectionProxy(con);

         }

//(5)这里需要通过connection进行相应的查询获得相应结果

         returnaction.doInConnection(conToUse);

      }

      catch (SQLExceptionex) {

         //释放 Connectionearly,避免潜在的连接池死锁

         DataSourceUtils.releaseConnection(con, getDataSource());

         con = null;

         throwgetExceptionTranslator().translate("ConnectionCallback",getSql(action), ex);

      }

      finally {

//4)释放 Connection early,避免潜在的连接池死锁

         DataSourceUtils.releaseConnection(con, getDataSource());

      }

   }

中间(1)DataSourceUtils.getConnection(dataSource);

其中真正获得connection的地方在:doGetConnection()中:

public staticConnection doGetConnection(DataSource dataSource) throwsSQLException {

      Assert.notNull(dataSource,"No DataSource specified");

//a从事务管理器中获得connection一个中间存放的列表holder,从其中取

      ConnectionHolderconHolder= (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

      if (conHolder !=null && (conHolder.hasConnection() ||conHolder.isSynchronizedWithTransaction())){

         conHolder.requested();

         if (!conHolder.hasConnection()) {

            logger.debug("Fetching resumed JDBC Connection fromDataSource");

      //如果holder中没有connection,则从dataSource中取,因为dataSource中一般我们会设置最小和最大的连接数

   conHolder.setConnection(dataSource.getConnection());

         }

         returnconHolder.getConnection();

      }

      // c如果holder中没有,且dataSource中也没有可以使用的,可以新创建,因为可能空闲时保有的connection数量较少,可以创建添加到pool

 

      logger.debug("Fetching JDBC Connection from DataSource");

      Connectioncon= dataSource.getConnection();

//是否需要同步处理

      if(TransactionSynchronizationManager.isSynchronizationActive()) {

         logger.debug("Registering transaction synchronization for JDBCConnection");

         ConnectionHolderholderToUse=conHolder;

         if (holderToUse ==null) {

            holderToUse = new ConnectionHolder(con);

         }

         else {

            holderToUse.setConnection(con);

         }

         holderToUse.requested();

         TransactionSynchronizationManager.registerSynchronization(

                newConnectionSynchronization(holderToUse,dataSource));

         holderToUse.setSynchronizedWithTransaction(true);

         if (holderToUse !=conHolder) {

            TransactionSynchronizationManager.bindResource(dataSource,holderToUse);

         }

      }

 

      return con;

   }

 

(2)  抽取本地jdbc连接,可以转换成oracle等其他的连接

他可以获得如图所示的相关的extractor,如c3p0,commonsDbcp,JBoss等

(3)或者是创建连接代理, also preparingreturned Statements.

            conToUse = createConnectionProxy(con);

 这一块可以参考相应的反射和代理,机器对应的模式

(4)realease释放connection

doReleaseConnection(con,dataSource);

public staticvoiddoReleaseConnection(Connectioncon,DataSourcedataSource)throwsSQLException {

      if (con ==null) {

         return;

      }//先从数据源中,获得connectionholder比较是否是该con,是的话released

      if (dataSource !=null) {

         ConnectionHolderconHolder= (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

         if (conHolder !=null && connectionEquals(conHolder,con)) {

            // 这里只是将连接数减1,并没有关闭,仍在pool.

            conHolder.released();

            return;

         }

      }

      logger.debug("Returning JDBC Connection to DataSource");

//没有数据源的话直接close

      doCloseConnection(con,dataSource);

   }

public voidreleased() {

      this.referenceCount--;

   }

这里是先判断是否有数据源,有的话利用connectionHolder关闭;而且这里的关闭并不是真正意义上的关闭,并没有调用如下的close方法。调用doCloseConnection()只是处理一下ConnectionHolder,将使用的资源数减1,真正的connection仍然存在,它由相关的数据源进行处理。

public staticvoidcloseConnection(Connectioncon){

      if (con !=null) {

         try {

            con.close();

         }

         catch (SQLExceptionex) {

            logger.debug("Could not close JDBC Connection",ex);

         }

         catch (Throwableex) {

            // We don't trust the JDBC driver: It might throwRuntimeException or Error.

            logger.debug("Unexpected exception on closing JDBCConnection",ex);

         }

      }

   }

 

public voidreleased() {

      super.released();

      if (!isOpen() &&this.currentConnection != null) {

      //这里release主要是执行了前面类似的内容

   this.connectionHandle.releaseConnection(this.currentConnection);

         this.currentConnection =null;

      }

   }

这样就可以看出和最初学习jdbc而没有框架的时候的释放关闭资源没有什么本质上的区别,connection真正释放的过程是一样的,只是这里因为业务和上升到框架角度需要考虑的额外内容、处理的额外事情多了一些。而且其中涉及到事务相应的内容,这暂且不讲。

 

讲了这么多这里还有一个ConnectionCallBack是什么东西?

找到其定义:泛化的回调接口,用来操作jdbcconnection的。可以使用任意一种类型和数量的statement,并且执行任意数量的操作在单独的connection上。

光看一个使用它作为参数的我们可能不了解,但是通过如下代码:

getJdbcTemplate().execute(newConnectionCallback<Object>() {

                public ObjectdoInConnection(Connectioncon)throwsSQLException, DataAccessException {

                   // 只是执行插入操作

                   PreparedStatementps =null;

                   try {

                      ps = con.prepareStatement(getInsertString());

                      setParameterValues(ps,values, getInsertTypes());

                      ps.executeUpdate();

                   }

                   finally {

                      JdbcUtils.closeStatement(ps);

                   }

                   //Get the key查询

                   StatementkeyStmt=null;

                   ResultSetrs =null;

                   Map<String, Object> keys = new HashMap<String,Object>(1);

                   try {

                      keyStmt =con.createStatement();

                      rs = keyStmt.executeQuery(keyQuery);

                      if (rs.next()) {

                         longkey = rs.getLong(1);

                         keys.put(getGeneratedKeyNames()[0],key);

                         keyHolder.getKeyList().add(keys);

                      }

                   }

                   finally {

                      JdbcUtils.closeResultSet(rs);

                      JdbcUtils.closeStatement(keyStmt);

                   }

                   returnnull;

                }

            });

         }

 

5//(5)这里需要通过connection进行相应的查询获得相应结果

         returnaction.doInConnection(conToUse);

DoInConnection中如(4)中所写的,那样connectionCallBack主要是操作connection,可以看出这里通过con获得了statement,然后进一步进行处理

 

(3)  jdbcTemplate方法中execute方法(StatementCallBack

statementCallBack:Interface,其功能和ConnectionCallBack接口类似,实现的接口主要是对于statement进行相关的操作

public <T> T execute(StatementCallback<T>action) throws DataAccessException {

      Assert.notNull(action,"Callback object must not be null");

 

      Connectioncon= DataSourceUtils.getConnection(getDataSource());

      Statementstmt= null;

      try {

         ConnectionconToUse= con;

         if (this.nativeJdbcExtractor!=null&&

               this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()){

            conToUse = this.nativeJdbcExtractor.getNativeConnection(con);

         }

         stmt = conToUse.createStatement();

         applyStatementSettings(stmt);

         StatementstmtToUse= stmt;

         if (this.nativeJdbcExtractor!=null){

            stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);

         }

         Tresult= action.doInStatement(stmtToUse);

         handleWarnings(stmt);

         returnresult;

      }

      catch (SQLExceptionex) {

         // Release Connection early, to avoid potentialconnection pool deadlock

         // in the case when the exception translator hasn't beeninitialized yet.

         JdbcUtils.closeStatement(stmt);

         stmt = null;

         DataSourceUtils.releaseConnection(con, getDataSource());

         con = null;

         throwgetExceptionTranslator().translate("StatementCallback",getSql(action), ex);

      }

      finally {

         JdbcUtils.closeStatement(stmt);

         DataSourceUtils.releaseConnection(con, getDataSource());

      }

   }

如果分析过ConnectionCallBack后则很容易理解,Tresult= action.doInStatement(stmtToUse);

通过如下代码:这里是内部类ExceuteStatementCallBack,实现了StatementCallBack接口,实现了其中DoInStatement方法,这种方式需要注意一下,在方法内部定义了ExecuteStatementCallBack类,实现了DoInStatement()方法,最后执行了executeStatementCallBack),执行了DoInStatement()方法。

public voidexecute(finalStringsql)throwsDataAccessException {

      if (logger.isDebugEnabled()) {

         logger.debug("Executing SQL statement [" +sql+ "]");

      }

      classExecuteStatementCallbackimplements StatementCallback<Object>, SqlProvider {

         public ObjectdoInStatement(Statement stmt) throws SQLException {

            stmt.execute(sql);

            return null;

         }

         public String getSql() {

            returnsql;

         }

      }

      execute(newExecuteStatementCallback());

   }

 

阅读代码我们可以看到好多的新东西,如asseert.notNull(sql,”xxxis not null”);

   if (logger.isDebugEnabled()) {

         logger.debug("Executing SQL query ["+sql+ "]");

      }

Assert 是断言语句可以用于测试如JUnit,而logger日志记录可以给我们提供一些信息提示。这对于开发和调试,测试等都是很重的工具。

 

(4)  jdbcTemplate 的方法有些地方我们看到如下代码

ResultSetExtractor   rse  ;returnrse.extractData(rsToUse);

它是结合了相应的数据库的驱动中对于ResultSet的处理的代码,同时结合了spring框架中的结果集进行了冲新的封装。形成相应的rowset,类似于resultset的一行数据,放置到一行rowset,每一行又分为若干个column,存放一个个的属性数据,可以进一步研究。

(5)  jdbcTemplate方法 批处理

这里讲解一个:public int[] batchUpdate(finalString[] sql)throwsDataAccessException {

      Assert.notEmpty(sql,"SQL array must not be empty");

      if (logger.isDebugEnabled()) {

         logger.debug("Executing SQL batch update of " +sql.length +" statements");

      }

//这里也是方法内部定义了一个实现相应接口的类,实现了其中重要的方法,doInStatement()方法

 

      class BatchUpdateStatementCallbackimplementsStatementCallback<int[]>, SqlProvider {

         privateString currSql;

         public int[]doInStatement(Statement stmt)throwsSQLException, DataAccessException {

            int[]rowsAffected = newint[sql.length];

            if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) {

                for (StringsqlStmt : sql) {

                   this.currSql =sqlStmt;

                   stmt.addBatch(sqlStmt);

                }

                rowsAffected =stmt.executeBatch();

            }

            else {

                for (inti = 0; i < sql.length; i++) {

                   this.currSql =sql[i];

                   if (!stmt.execute(sql[i])) {

                      rowsAffected[i] =stmt.getUpdateCount();

                   }

                   else {

                      thrownewInvalidDataAccessApiUsageException("Invalidbatch SQL statement: " +sql[i]);

                   }

                }

            }

            returnrowsAffected;

         }

         publicString getSql() {

            return this.currSql;

         }

      }

      return execute(newBatchUpdateStatementCallback());

   }

 

(6)PreparedStatementCreator顾名思义就是用来创建相应statement的,它是一个接口,如下面的实现 public PreparedStatementcreatePreparedStatement(Connectioncon) throwsSQLException方法

private staticclassSimplePreparedStatementCreatorimplements PreparedStatementCreator, SqlProvider {

 

      private final String sql;

 

      public SimplePreparedStatementCreator(String sql) {

         Assert.notNull(sql,"SQL must not be null");

         this.sql =sql;

      }

 

      public PreparedStatementcreatePreparedStatement(Connectioncon) throwsSQLException {

         returncon.prepareStatement(this.sql);

      }

 

      public String getSql() {

         return this.sql;

      }

 

他的操作也和上面相类似,一般就是在方法内部声明该类,实现其中的方法,然后进行下一步的执行调用。

 

另外:preparedStatement继承自statement,但是有何它有些不同之处:

(a)  preparedStatement实例中包含已经编译的sql语句,其sql可以包含一个或者是多个in参数,他们在创建时未被指定,只是一?作为占位符,但是在执行之前必须为其指定相应的数据,可以通过setXXX方法提供数据。

(b)由于preparedstatement对象已经预编译过,其执行的速度要快于statement对象。因此多次执行的sql语句经常创建为preparedstatement对象,提高效率。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值