国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

香港云服务器
服務器之家 - 數據庫 - PostgreSQL - Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

2020-04-28 14:49小燈光環 PostgreSQL

這篇文章主要介紹了mybatis調用postgresql自定義函數傳遞數組參數的解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

項目中用到了Mybatis調用PostgreSQL存儲過程(自定義函數)相關操作,由于PostgreSQL自帶數組類型,所以有一個自定義函數的入參就是一個int數組,形如:

 

復制代碼 代碼如下:
CREATE OR REPLACE FUNCTION "public"."func_arr_update"(ids _int4)...


如上所示,參數是一個int數組,Mybatis提供了對調用存儲過程的支持,那么PostgreSQL獨有的數組類型作為存儲過程的參數又將如何處理呢?其實很簡單,mybatis提供了typeHandlers可以創建一個數組類型的類型處理器,具體做法為:實現 org.apache.ibatis.type.TypeHandler 接口, 或繼承一個很便利的類 org.apache.ibatis.type.BaseTypeHandler, 然后可以選擇性地將它映射到一個 JDBC 類型,先稍作了解,后面再做詳細說明,接下來依舊結合一個示例來看看。

 

創建自定義函數

如圖,第一步首先是創建一個用于調用的自定義函數,功能也很簡單,遍歷參數數組的每一個元素和t_student表的stuid做比較,若一致,則修改那條記錄的stuname(在其后拼接一段字符串),該自定義函數的DLL語句如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE OR REPLACE FUNCTION "public"."func_arr_update"(ids _int4)
 RETURNS "pg_catalog"."void" AS $BODY$
DECLARE
   scount INTEGER;
   rownum integer := 1;
BEGIN
    scount:=array_length(ids,1);
    while rownum <= scount LOOP
      update t_student set stuname = stuname || ' has been modified. ' where stuid = ids[rownum];
      rownum := rownum + 1;
  END LOOP;
  RETURN;
END
$BODY$
 LANGUAGE 'plpgsql' VOLATILE COST 100
;
 
ALTER FUNCTION "public"."func_arr_update"(ids _int4) OWNER TO "postgres";

很簡單,獲取到參數數組的長度后開始循環,匹配stuid并更新stuname,直接在數據庫調用一下看看結果:

Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

如上圖,可以看到成功修改了stuid為101,102和103的stuname,自定義函數已經沒問題了,接下來就具體看一下如何通過mybatis調用。

調用自定義函數

mybatis中調用自定義函數很簡單,Mapper XML文件中的select元素直接提供了屬性支持——statementType,在官方文檔中可以看到:

Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

如上圖,statementType的值默認是PREPARED,也就是說底層默認會使用jdbc的PreparedStatement,而我們都知道jdbc調用存儲過程時需要用CallableStatement,所以在這里我們需要將statementType的值設置為CALLABLE。

mybatis默認的ArrayTypeHandler

調用存儲過程很簡單,那么接下來的問題是如何在mybatis中傳一個數組參數到存儲過程中呢?這里就要用到另外一個概念——TypeHandler,這是mybatis提供的自定義類型轉換器,mybatis在預編譯語句對象(PreparedStatement)設置參數時或是從結果集中取值時都會用類型處理器將獲取的值以合適的方式轉換成Java類型,mybatis默認實現了一部分TypeHandler供我們使用,當我們沒有指定TypeHandler時(大多數情況都不會指定),mybatis會根據參數或者返回結果的不同,默認為我們選擇合適的TypeHandler處理,下面可以通過查看源碼大概看一下默認的TypeHandler,導入源碼后可以在org.apache.ibatis.type包下找到一個TypeHandlerRegistry類,typeHandler正是通過這個類管理的,先看一下它的構造方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public TypeHandlerRegistry() {
 register(Boolean.class, new BooleanTypeHandler());
 register(boolean.class, new BooleanTypeHandler());
 register(JdbcType.BOOLEAN, new BooleanTypeHandler());
 register(JdbcType.BIT, new BooleanTypeHandler());
 
 register(Byte.class, new ByteTypeHandler());
 register(byte.class, new ByteTypeHandler());
 register(JdbcType.TINYINT, new ByteTypeHandler());
 
 register(Short.class, new ShortTypeHandler());
 register(short.class, new ShortTypeHandler());
 register(JdbcType.SMALLINT, new ShortTypeHandler());
 
 register(Integer.class, new IntegerTypeHandler());
 register(int.class, new IntegerTypeHandler());
 register(JdbcType.INTEGER, new IntegerTypeHandler());
 
 register(Long.class, new LongTypeHandler());
 register(long.class, new LongTypeHandler());
 
 register(Float.class, new FloatTypeHandler());
 register(float.class, new FloatTypeHandler());
 register(JdbcType.FLOAT, new FloatTypeHandler());
 
 register(Double.class, new DoubleTypeHandler());
 register(double.class, new DoubleTypeHandler());
 register(JdbcType.DOUBLE, new DoubleTypeHandler());
 
 register(String.class, new StringTypeHandler());
 register(String.class, JdbcType.CHAR, new StringTypeHandler());
 register(String.class, JdbcType.CLOB, new ClobTypeHandler());
 register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
 register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
 register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
 register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
 register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
 register(JdbcType.CHAR, new StringTypeHandler());
 register(JdbcType.VARCHAR, new StringTypeHandler());
 register(JdbcType.CLOB, new ClobTypeHandler());
 register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
 register(JdbcType.NVARCHAR, new NStringTypeHandler());
 register(JdbcType.NCHAR, new NStringTypeHandler());
 register(JdbcType.NCLOB, new NClobTypeHandler());
 
 register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
 register(JdbcType.ARRAY, new ArrayTypeHandler());
 
 register(BigInteger.class, new BigIntegerTypeHandler());
 register(JdbcType.BIGINT, new LongTypeHandler());
 
 register(BigDecimal.class, new BigDecimalTypeHandler());
 register(JdbcType.REAL, new BigDecimalTypeHandler());
 register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
 register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
 
 register(Byte[].class, new ByteObjectArrayTypeHandler());
 register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
 register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
 register(byte[].class, new ByteArrayTypeHandler());
 register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
 register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
 register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
 register(JdbcType.BLOB, new BlobTypeHandler());
 
 register(Object.class, UNKNOWN_TYPE_HANDLER);
 register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
 register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
 
 register(Date.class, new DateTypeHandler());
 register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
 register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
 register(JdbcType.TIMESTAMP, new DateTypeHandler());
 register(JdbcType.DATE, new DateOnlyTypeHandler());
 register(JdbcType.TIME, new TimeOnlyTypeHandler());
 
 register(java.sql.Date.class, new SqlDateTypeHandler());
 register(java.sql.Time.class, new SqlTimeTypeHandler());
 register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
 
 // issue #273
 register(Character.class, new CharacterTypeHandler());
 register(char.class, new CharacterTypeHandler());
}

如上所示,這就是全部默認的typeHandler了,注意一下46,47行可以看到默認有一個ArrayTypeHandler,順便看一下它的源碼:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
 *  Copyright 2009-2012 The MyBatis Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.ibatis.type;
 
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
public class ArrayTypeHandler extends BaseTypeHandler<Object> {
 
 public ArrayTypeHandler() {
  super();
 }
 
 @Override
 public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
  ps.setArray(i, (Array) parameter);
 }
 
 @Override
 public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
  Array array = rs.getArray(columnName);
  return array == null ? null : array.getArray();
 }
 
 @Override
 public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  Array array = rs.getArray(columnIndex);
  return array == null ? null : array.getArray();
 }
 
 @Override
 public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  Array array = cs.getArray(columnIndex);
  return array == null ? null : array.getArray();
 }
 
}

那它能否識別PostgreSQL的數組類型并將它自動轉換成Java數組類型呢?按官方的說法,既然這是默認的typeHandler,那么我們無需做任何配置mybatis會自動嘗試適配,所以直接寫測試代碼看看:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testFunc1() {
  SqlSession session = sqlSessionFactory.openSession();
  try {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("ids", new Integer[] { 101, 102, 103 });
    session.update("com.wl.entity.StudentMapper.testFuncUpdate2", map);
    session.commit();
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    session.close();
  }
}
?
1
2
3
<update id="testFuncUpdate2" statementType="CALLABLE">
  {call func_arr_update (#{ids,mode=IN})}
</update>

如上所示,參數傳的是一個Integer[],直接運行一下junit看看測試結果:

Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

Can't infer the SQL type to use for an instance of [Ljava.lang.Integer;. Use setObject() with an explicit Types value to specify the type to use.

異常log如上所示,在調用AbstractJdbc2Statement類的setObject方法時拋出異常,那么再看看這個方法的源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
 * This stores an Object into a parameter.
 */
public void setObject(int parameterIndex, Object x) throws SQLException
{
  checkClosed();
  if (x == null)
    setNull(parameterIndex, Types.OTHER);
  else if (x instanceof String)
    setString(parameterIndex, (String)x);
  else if (x instanceof BigDecimal)
    setBigDecimal(parameterIndex, (BigDecimal)x);
  else if (x instanceof Short)
    setShort(parameterIndex, ((Short)x).shortValue());
  else if (x instanceof Integer)
    setInt(parameterIndex, ((Integer)x).intValue());
  else if (x instanceof Long)
    setLong(parameterIndex, ((Long)x).longValue());
  else if (x instanceof Float)
    setFloat(parameterIndex, ((Float)x).floatValue());
  else if (x instanceof Double)
    setDouble(parameterIndex, ((Double)x).doubleValue());
  else if (x instanceof byte[])
    setBytes(parameterIndex, (byte[])x);
  else if (x instanceof java.sql.Date)
    setDate(parameterIndex, (java.sql.Date)x);
  else if (x instanceof Time)
    setTime(parameterIndex, (Time)x);
  else if (x instanceof Timestamp)
    setTimestamp(parameterIndex, (Timestamp)x);
  else if (x instanceof Boolean)
    setBoolean(parameterIndex, ((Boolean)x).booleanValue());
  else if (x instanceof Byte)
    setByte(parameterIndex, ((Byte)x).byteValue());
  else if (x instanceof Blob)
    setBlob(parameterIndex, (Blob)x);
  else if (x instanceof Clob)
    setClob(parameterIndex, (Clob)x);
  else if (x instanceof Array)
    setArray(parameterIndex, (Array)x);
  else if (x instanceof PGobject)
    setPGobject(parameterIndex, (PGobject)x);
  else if (x instanceof Character)
    setString(parameterIndex, ((Character)x).toString());
  else if (x instanceof Map)
    setMap(parameterIndex, (Map)x);
  else
  {
    // Can't infer a type.
    throw new PSQLException(GT.tr("Can''t infer the SQL type to use for an instance of {0}. Use setObject() with an explicit Types value to specify the type to use.", x.getClass().getName()), PSQLState.INVALID_PARAMETER_TYPE);
  }
}

我們參數傳進去的Integer[]數組是一個Object數組,而 setObject(int parameterIndex, Object x)方法的第二個參數是Object,所以這里這里自然無法匹配也就報錯了,那么換成int[]可以嗎?在上面的else if語句中明顯沒有x instanceof int[]這行代碼,所以當然也不行,說到這里也就明確了mybatis默認提供的ArrayTypeHandler是無法自動識別PostgreSQL的數組類型,我們必須自定義一個參數為Object[]的ArrayTypeHandler才能實現匹配。

自定義ArrayTypeHandler

如題,先貼上代碼:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.wl.util;
 
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.TypeException;
 
@MappedJdbcTypes(JdbcType.ARRAY)
public class ArrayTypeHandler extends BaseTypeHandler<Object[]> {
 
  private static final String TYPE_NAME_VARCHAR = "varchar";
  private static final String TYPE_NAME_INTEGER = "integer";
  private static final String TYPE_NAME_BOOLEAN = "boolean";
  private static final String TYPE_NAME_NUMERIC = "numeric";
 
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i,
      Object[] parameter, JdbcType jdbcType) throws SQLException {
 
    String typeName = null;
    if (parameter instanceof Integer[]) {
      typeName = TYPE_NAME_INTEGER;
    } else if (parameter instanceof String[]) {
      typeName = TYPE_NAME_VARCHAR;
    } else if (parameter instanceof Boolean[]) {
      typeName = TYPE_NAME_BOOLEAN;
    } else if (parameter instanceof Double[]) {
      typeName = TYPE_NAME_NUMERIC;
    }
 
    if (typeName == null) {
      throw new TypeException(
          "ArrayTypeHandler parameter typeName error, your type is "
              + parameter.getClass().getName());
    }
 
    Connection conn = ps.getConnection();
    Array array = conn.createArrayOf(typeName, parameter);
    ps.setArray(i, array);
  }
 
  @Override
  public Object[] getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
 
    return getArray(rs.getArray(columnName));
  }
 
  @Override
  public Object[] getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
 
    return getArray(rs.getArray(columnIndex));
  }
 
  @Override
  public Object[] getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
 
    return getArray(cs.getArray(columnIndex));
  }
 
  private Object[] getArray(Array array) {
 
    if (array == null) {
      return null;
    }
 
    try {
      return (Object[]) array.getArray();
    } catch (Exception e) {
    }
 
    return null;
  }
}

如上所示,我們指定了參數類型為Object[],這樣就可以接收Integer[]類型的參數了,關鍵是44~46行,postgresql的驅動類AbstractJdbc4Connection實現了Connect接口的createArrayOf方法,源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Array createArrayOf(String typeName, Object[] elements) throws SQLException
{
  checkClosed();
  int oid = getTypeInfo().getPGArrayType(typeName);
  if (oid == Oid.UNSPECIFIED)
    throw new PSQLException(GT.tr("Unable to find server array type for provided name {0}.", typeName), PSQLState.INVALID_NAME);
 
  char delim = getTypeInfo().getArrayDelimiter(oid);
  StringBuffer sb = new StringBuffer();
  appendArray(sb, elements, delim);
 
  // This will not work once we have a JDBC 5,
  // but it'll do for now.
  return new Jdbc4Array(this, oid, sb.toString());
}

這樣通過自定義的ArrayTypeHandler就可以在Mybatis中方便的操作數組類型數據了,最后再測試一下,測試類代碼不變,僅需在調用存儲過程時指定mapper文件的typeHandler即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testFunc1() {
  SqlSession session = sqlSessionFactory.openSession();
  try {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("ids", new Integer[] { 101, 102, 103 });
    session.update("com.wl.entity.StudentMapper.testFuncUpdate2", map);
    session.commit();
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    session.close();
  }
}
?
1
2
3
<update id="testFuncUpdate2" statementType="CALLABLE">
  {call func_arr_update (#{ids,mode=IN,typeHandler=com.wl.util.ArrayTypeHandler})}
</update>

再次運行junit看一下測試結果:

Mybatis調用PostgreSQL存儲過程實現數組入參傳遞

如上所示,此時已經可以成功調用參數為Integer[]數組的pg自定義函數了。

總結

簡單記錄一下在mybatis中調用postgresql自定義函數時傳遞數組參數的解決方案,希望對遇到同樣問題的朋友有所幫助,The End。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/wlwlwlwl015/article/details/52526630

延伸 · 閱讀

精彩推薦
474
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 日韩视频www| 99在线播放 | 男人天堂视频网 | 成人黄色片网站 | 欧美视频一二三区 | 国产精品成人一区二区三区夜夜夜 | 亚洲乱码国产乱码精品精的特点 | 午夜国产| 亚洲视频在线观看网址 | 欧美日韩激情一区 | 999国产在线| 一级黄色片视频 | 久久久久这里只有精品 | 亚洲天堂一区二区 | 欧美日韩中文 | 日韩欧美精品 | 免费看黄在线 | 免费成人看片 | 国产成人一区二区三区 | 国产精品一区久久久久 | 日本精品视频一区二区 | 国产精品久久久久久久久久久久久久 | 久久久久久久成人 | 中文字幕一区二区三区在线观看 | 国产成人久久 | 精品国产一区二区三区在线观看 | 国产精品久久久久久久午夜片 | 久久久久久久国产 | 精品在线一区二区 | 国产精品久久久久久吹潮 | 国产欧美久久一区二区三区 | 玖玖操 | 福利在线播放 | 久久91久久久久麻豆精品 | 成人午夜在线 | 精品久久久久久久久久久久 | 日韩一区在线观看视频 | 精久久| 毛片一区| 日韩精品免费在线观看 | 亚洲视频欧美视频 |