Spring JdbcTemplate call Oracle stored procedure with the Oracle driver download

table of Contents

Foreword

Oracle driver download

Oracle Data Source Configuration

execute a stored procedure call no return value

execute single return value stored procedure call

execute a stored procedure call returns a result set

call method call a stored procedure


Foreword

1, on the introduction of JdbcTemplate, pom-dependent, DI injection can refer to " the Spring JdbcTemplate template analysis of the common CRUD " The paper presents the JdbcTemplate call database stored procedures , although Mysql also has a stored procedure, but to more than cover little as possible , we choose to call the Oracle stored procedure, other databases is the same reason.

1) execute method: can execute any SQL statement, generally used for the implementation of DDL statements; and to build tables, delete tables, etc. SQL.
2) Update, batchUpdate method: update methods for performing add, modify and delete statements; batchUpdate the method used to execute a batch of related statements;
3) queryForObject, queryForList, query method: to perform a query related statements; queryForObject query result can only be an excess or less will throw an exception;
4) queryForList with query results is empty, return the list size is 0, not NullPointerException.
5) call method: stored for performing a procedure, a function related statements.

2, this environment: Oracle 11g (drive version ojdbc8-19.3.0.0) + Java JDK 1.8 + Spring Boot 2.1.5 + + IDEA 2018.

3, JdbcTemplate JDBC itself is lightweight package, so call a stored procedure is similar " JDBC call a stored procedure / function ."

4, in order to facilitate testing, prepare ahead of time data: prepare employees table and the department table of test data

Oracle driver download

1, unlike the open source Mysql, Oracle a fee, usually can not be successfully downloaded from Oracle rely on Maven central repository above , Oracle can only go directly to the official website to download:

<!-- https://mvnrepository.com/artifact/com.oracle.jdbc/ojdbc8 -->
<!-- oracle 官网提供的驱动依赖,通常都会下载失败,需要手动在本地仓库进行 mvn install 安装-->
 <dependency>
     <groupId>com.oracle.jdbc</groupId>
     <artifactId>ojdbc8</artifactId>
     <version>12.2.0.1</version>
 </dependency>

1.6 ojdbc6 match the JDK, ojdbc8 1.8 match the JDK , ojdbc10 match jdk 10 and so on, and ojdbc14 match is jdk1.4.

2, solve way: there are good people and organizations to share some available for download, and the official website of the equivalent maven dependent on the network, such as the following (effective pro-test):

<!-- https://mvnrepository.com/artifact/com.github.noraui/ojdbc8 -->
<!--这是网络上的雷锋提供的开源 ojdbc8 驱动,功能与官网的是一样的,专门用于替代 Oracle 官网 maven 下载失败-->
<dependency>
    <groupId>com.github.noraui</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>12.2.0.1</version>
</dependency>

3, solve way: Although you can not download from maven central repository ojdbc drive, but can be downloaded directly from the jar package Oracle official website.

3.1, starting with the official website to download ojdbc8.jar package : https://www.oracle.com/database/technologies/jdbc-ucp-122-downloads.html

Download them home from Oracle's official website is something you need to log in, so if there is no account, you need to register, download the driver free of charge.

3.3, then use the following mvn install command to deploy the project , it will automatically be stored in a local warehouse:

mvn install:install-file -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -DgeneratePom=true -Dfile=C:\Users\Think\Downloads\ojdbc8.jar

-DgroupId: groupId the specified value, you can customize, consistent with recommendations to the official website of people
-DartifactId: The value specified artifactId, you can customize recommendations are consistent with the others to the official website of
-Dversion: specified value version, he may defined, it is recommended to people with the same official website, also available from open ojdbc8.jar, which MANIFEST.MF file can also see the version number
-Dpackaging: specify the type of packaging, such as jar, war package
-DgeneratePom: Specifies whether the generated pom.xml file, specify it generates
-Dfile: the need to deploy the jar package file path

Oracle Data Source Configuration

1, before beginning the preparation of the following code, specify the data source arranged in the global configuration file:

#数据源配置
spring:
  profiles: oracleDb
  datasource:
    username: hnbs_3
    password: 1
    driverClassName: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@127.0.0.1:1521:ORCL

More configuration item can refer to the official website: https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#common-application-properties

Or: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.java

execute a stored procedure call no return value

1, Mysql there are " drop the Table table name if exists operation", delete the table exists only when, Oracle and if the judge does not exists, it is deleted directly, if the table does not exist, "the Table drop table" on I will complain,

2, here using a stored procedure to solve this problem, Oracle database stored procedure to prepare the following function is passed as a parameter table name, if the table already exists, delete it, or do not operate , the stored procedure without return.

--表名作为参数,如果表已经存在,则删除它。
create or replace procedure pro_drop_table_by_name(tableName in user_tables.TABLE_NAME%type)
is
  flag number := 0; --表是否存在的表示,大于0表示表已经存在
begin
    --user_tables 是系统定义的视图,可以查看当前用户下的所有表信息,表中的表名区分大小写,而且是大写
    select count(1) into flag from user_tables where table_name = upper(tableName) ;
    if flag > 0 then
        execute immediate 'drop table '|| tableName ;--如果表已经存在,则删除它
    end if;
end;

-- 数据库中调用存储过程:call pro_drop_table_by_name('student');

Note: Here To ensure data integrity, and did not do cascading deletes, that is when the data table is deleted if it is referenced in another table, you will get an error when deleting

    /**
     * 删除表
     * http://localhost:8080/emp/dropTable?tableName=emp
     * http://localhost:8080/emp/dropTable?tableName=dept
     * emp 员工表引用了 dept 部门表,如果先删除 dept 表,其中有数据被 emp 表引用,则删除时报错:
     * oracle.jdbc.OracleDatabaseException: ORA-02449: 表中的唯一/主键被外键引用
     *
     * @param tableName
     * @return
     */
    @GetMapping("emp/dropTable")
    public String dropTableByName(@RequestParam String tableName) {
        JsonObject jsonObject = new JsonObject();
        try {
            //sql 和在数据库中完全一样
            String sql = "call pro_drop_table_by_name('" + tableName + "')";
            jdbcTemplate.execute(sql);

            jsonObject.addProperty("code", 200);
            jsonObject.addProperty("message", sql);
        } catch (DataAccessException e) {
            logger.error(e.getMessage(), e);
            jsonObject.addProperty("code", 500);
            jsonObject.addProperty("message", e.getMessage());
        }
        return jsonObject.toString();
    }

execute single return value stored procedure call

1, Oracle database stored in the preparation process is as follows:

--表名作为参数,同时指定返回参数,如果表名存在,则返回 1,不存在返回 0
create or replace procedure pro_check_table_by_name(tableName in user_tables.TABLE_NAME%type, ifExists out number) is
begin
    --user_tables 是系统定义的视图,可以查看当前用户下的所有表信息,表中的表名区分大小写,而且是大写
    select count(1) into ifExists from user_tables where table_name = upper(tableName) ;
end;

-- 数据库中调用存储过程:
declare
   tableName varchar2(30) := 'demp'; //被检查的表名
   ifExists number; //返回参数
begin
  pro_check_table_by_name(tableName,ifExists);
  dbms_output.put_line(ifExists);//打印返回值
end;

2, execute (CallableStatementCreator csc, CallableStatementCallback <T> action) is the underlying call a stored procedure " JDBC call a stored procedure / function ", so to write exactly the same, to create java.sql.CallableStatement CallableStatementCreator, make the call and get the return value in CallableStatementCallback .

/**
 * 检查某个表在数据库中是否已经存在,存在时返回1,否则返回0
 * http://localhost:8080/emp/checkTableByName?tableName=emp
 *
 * @param tableName
 * @return
 */
@GetMapping("emp/checkTableByName")
public Integer checkTableByName(@RequestParam String tableName) {
	Integer execute = (Integer) jdbcTemplate.execute(new CallableStatementCreator() {

		//创建可回调语句,方法里面就是纯 jdbc 创建调用存储的写法
		@Override
		public CallableStatement createCallableStatement(Connection connection) throws SQLException {
			//存储过程调用 sql,通过 java.sql.Connection.prepareCall 获取回调语句
			String sql = "call pro_check_table_by_name(?,?)";
			CallableStatement callableStatement = connection.prepareCall(sql);
			//设置第一个占位符参数值,传入参数。参数索引从1开始
			callableStatement.setString(1, tableName);
			//注册第二个参数(返回值)的数据类型
			callableStatement.registerOutParameter(2, OracleTypes.INTEGER);
			return callableStatement;
		}
	}, new CallableStatementCallback<Object>() {
		//正式调用存储过程以及处理返回的值.
		@Override
		public Object doInCallableStatement(CallableStatement callableStatement) throws SQLException {
			//执行调用存储过程
			callableStatement.execute();
			//参数索引从1开始,获取村存储过程的返回值.
			return callableStatement.getInt(2);
		}
	});
	return execute;
}

execute a stored procedure call returns a result set

1, the data stored prepared as follows:


--创建存储过程,用于分页查询
--传入参数:pageNo 查询的页码,pageSize 每页的条数;输出参数:vrows 使用一个引用游标用于接收多条结果集。普通游标无法做到,只能使用引用游标
create or replace procedure pro_query_emp_limit(pageNo in number,pageSize in number,vrows out sys_refcursor) is
begin
  --存储过程中只进行打开游标,将 select 查询出的所有数据放置到 vrows 游标中,让调用着进行获取
open vrows for select t.empno,t.ename,t.job,t.mgr,t.hiredate,t.sal,t.comm,t.deptno from (select rownum r,t1.* from emp t1) t 
     where t.r between ((pageNo-1) * pageSize+1) and pageNo * pageSize;
end;

--数据库中使用引用游标读取上面的存储过程返回的值。下面只是加深理解,和 java 调用无关
declare
     vrows sys_refcursor ;--声明引用游标
     vrow emp%rowtype; --定义变量接收遍历到的每一行数据
begin
     pro_query_emp_limit(5,3,vrows);--调用存储过程
     loop 
       fetch vrows into vrow; -- fetch into 获取游标的值
       exit when vrows%notfound; -- 如果没有获取到值,则退出循环
       dbms_output.put_line('姓名:'|| vrow.ename || ' 薪水:'|| vrow.sal);
     end loop;
end;

2, call the above and singled out the results returned are basically the same, the difference is the result of the returned result set into a ResultSet:

/**
 * 存储过程实现分页查询,传入页码和条数即可进行分页返回
 * http://localhost:8080/emp/pageQuery?pageNo=2&pageSize=5
 *
 * @param pageNo   页码
 * @param pageSize 每页显示的条数
 * @return
 */
@GetMapping("emp/pageQuery")
public List pageQuery(@RequestParam Integer pageNo, @RequestParam Integer pageSize) {
	List execute = (List) jdbcTemplate.execute(new CallableStatementCreator() {

		//创建可回调语句,方法里面就是纯 jdbc 创建调用存储的写法
		@Override
		public CallableStatement createCallableStatement(Connection connection) throws SQLException {
			//存储过程调用 sql,通过 java.sql.Connection.prepareCall 获取回调语句,sql 外围可以花括号括起来
			String sql = "{call pro_query_emp_limit(?,?,?)}";
			CallableStatement callableStatement = connection.prepareCall(sql);
			//设置第占位符参数值
			callableStatement.setInt(1, pageNo);
			callableStatement.setInt(2, pageSize);
			//输出参数类型设置为引用游标
			callableStatement.registerOutParameter(3, OracleTypes.CURSOR);
			return callableStatement;
		}
	}, new CallableStatementCallback<Object>() {
		//正式调用存储过程以及处理返回的值.
		@Override
		public Object doInCallableStatement(CallableStatement callableStatement) throws SQLException {
			//存储返回结果
			List<Map<String, Object>> resultMapList = new ArrayList<>(8);
			//遍历时临时对象
			Map<String, Object> temp;
			//执行调用存储过程,将结果转为 java.sql.ResultSet 结果集
			callableStatement.execute();
			ResultSet resultSet = (ResultSet) callableStatement.getObject(3);

			//遍历结果集
			while (resultSet.next()) {
				temp = new HashMap<>(8);
				//根据字段名称取值
				temp.put("empno", resultSet.getInt("empno"));
				temp.put("ename", resultSet.getString("ename"));
				temp.put("job", resultSet.getString("job"));
				temp.put("mgr", resultSet.getInt("mgr"));
				temp.put("hiredate", resultSet.getDate("hiredate"));
				temp.put("sal", resultSet.getFloat("sal"));
				resultMapList.add(temp);
			}
			return resultMapList;
		}
	});
	return execute;
}

call method call a stored procedure

1, it has been said opening call a special method for executing stored procedures, functions related statements. call method to return the results to further execute on the basis of the package, only you need to create CallableStatement can, do not care about the result of the conversion.

2, exexute to use the callback CallableStatementCallback List <SqlParameter>, each of which corresponds to a sequence SqlParameter placeholder parameters.

SqlParameter 表示存储过程的传入参数,可以不指定参数名称,但是必须指定参数类型
SqlOutParameter 表示存储过程的输出参数,必须指定名称和类型,名称自定义即可,会被作为返回值存放在 map 中

3, instead call the following methods to achieve the above functions, using stored procedures to check a table in the database already exists, exists returns 1, otherwise it returns 0, using the call method call:

/**
 * 存储过程检查某个表在数据库中是否已经存在,存在时返回1,否则返回0,使用 call 方法进行调用
 * http://localhost:8080/emp/callCheckTableByName?tableName=emp
 *
 * @param tableName
 * @return
 */
@GetMapping("emp/callCheckTableByName")
@SuppressWarnings("all")
public Map<String, Object> callCheckTableByName(@RequestParam String tableName) {

	//SqlParameter 表示存储过程的传入参数,可以不指定参数名称,但是必须指定参数类型
	//SqlOutParameter 表示存储过程的输出参数,必须指定名称和类型,名称自定义即可,会被作为返回值存放在 map 中
	List<SqlParameter> sqlParameterList = new ArrayList<>(4);
	sqlParameterList.add(new SqlParameter(OracleTypes.VARCHAR));
	sqlParameterList.add(new SqlOutParameter(tableName, OracleTypes.NUMBER));

	//call 方法在 execute 的基础上对返回结果进行进一步的封装,只需要创建 CallableStatement
	//List<SqlParameter> 中的每一个 SqlParameter 按顺序对应占位符参数
	//返回的 map 包含返回参数
	Map<String, Object> call = jdbcTemplate.call(new CallableStatementCreator() {
		@Override
		public CallableStatement createCallableStatement(Connection connection) throws SQLException {
			//存储过程调用 sql,通过 java.sql.Connection.prepareCall 获取回调语句,sql 外围可以花括号括起来
			String sql = "{call pro_check_table_by_name(?,?)}";
			CallableStatement callableStatement = connection.prepareCall(sql);
			//设置第一个占位符参数值,传入参数。参数索引从1开始
			callableStatement.setString(1, tableName);
			//注册第二个参数(返回值)的数据类型,oracle.jdbc.OracleTypes 中定义了全部的数据类型常量
			callableStatement.registerOutParameter(2, OracleTypes.INTEGER);
			return callableStatement;
		}
	}, sqlParameterList);
	return call;
}

4, using the method call a stored procedure call paging queries, after a call for the use of the cursor returned much more convenient, no longer need to own a a value, and it will be converted, the recommended way to automatically:

/**
 * 使用 call 方法调用存储过程进行分页查询,推荐方式
 * http://localhost:8080/emp/callPageQuery?pageNo=2&pageSize=5
 *
 * @param pageNo
 * @param pageSize
 * @return
 */
@GetMapping("emp/callPageQuery")
@SuppressWarnings("all")
public List<Map<String, Object>> callPageQuery(@RequestParam Integer pageNo, @RequestParam Integer pageSize) {
	//设置存储过程参数
	//SqlParameter 表示存储过程的传入参数,可以不知道参数名称,但是必须指定参数类型
	//SqlOutParameter 表示存储过程的输出参数,必须指定名称和类型,名称自定义即可,会被作为返回值存放在 map 中
	List<SqlParameter> sqlParameterList = new ArrayList<>(4);
	sqlParameterList.add(new SqlParameter(OracleTypes.NUMBER));
	sqlParameterList.add(new SqlParameter(OracleTypes.NUMBER));
	sqlParameterList.add(new SqlOutParameter("resultSet", OracleTypes.CURSOR));

	//使用了 call 之后对于返回的游标就方便多了,不再需要自己一个一个取值了,它会自动进行转换
	//call 的 key 会是 resultSet,然后它的值会是一个 List<Map>,自动转换好了
	Map<String, Object> call = jdbcTemplate.call(new CallableStatementCreator() {
		//创建可回调语句,方法里面就是纯 jdbc 创建调用存储的写法
		@Override
		public CallableStatement createCallableStatement(Connection connection) throws SQLException {
			//存储过程调用 sql,通过 java.sql.Connection.prepareCall 获取回调语句,sql 外围可以花括号括起来
			String sql = "{call pro_query_emp_limit(?,?,?)}";
			CallableStatement callableStatement = connection.prepareCall(sql);
			//设置第占位符参数值
			callableStatement.setInt(1, pageNo);
			callableStatement.setInt(2, pageSize);
			//输出参数类型设置为引用游标
			callableStatement.registerOutParameter(3, OracleTypes.CURSOR);
			return callableStatement;
		}
	}, sqlParameterList);

	//没有值时就是空 list,不会控制在异常
	List<Map<String, Object>> dataList = (List<Map<String, Object>>) call.get("resultSet");
	return dataList;
}

Source: https://github.com/wangmaoxiong/jdbc_template_app

Published 456 original articles · won praise 987 · Views 1.08 million +

Guess you like

Origin blog.csdn.net/wangmx1993328/article/details/105156869