1. 程式人生 > 實用技巧 >java後端通過資料庫地址等資訊訪問第三方資料庫,以及JdbcTemplate的in語法引數解決辦法

java後端通過資料庫地址等資訊訪問第三方資料庫,以及JdbcTemplate的in語法引數解決辦法

一、通過資料庫資訊訪問第三方資料庫

本例子是從一張儲存資料庫資訊表中查出第三方資料庫資訊,再進行訪問的

1、 對資料庫密碼的加密、解密類

public class SecretHelper {

    public byte[] random(int len) {
        byte[] buf = new byte[len];
        random.nextBytes(buf);
        return buf;
    }

    public byte[] password(byte[] plain) throws GeneralSecurityException {
        Mac mac 
= macKeysetHandle.getPrimitive(Mac.class); return mac.computeMac(plain); } public boolean verify(byte[] cipher, byte[] plain) { try { Mac mac = macKeysetHandle.getPrimitive(Mac.class); mac.verifyMac(cipher, plain); return true; } catch
(GeneralSecurityException e) { logger.error("fail on verify password ", e); } return false; } public byte[] encrypt(byte[] plain, byte[] salt) throws GeneralSecurityException { Aead aead = aesKeysetHandle.getPrimitive(Aead.class); return aead.encrypt(plain, salt); }
public byte[] decrypt(byte[] cipher, byte[] salt) throws GeneralSecurityException { Aead aead = aesKeysetHandle.getPrimitive(Aead.class); return aead.decrypt(cipher, salt); } @PostConstruct void init() throws IOException, GeneralSecurityException { random = new Random(); final var aes = new File("aes.json"); if (aes.exists()) { logger.info("load aes key from {}", aes); aesKeysetHandle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(aes)); } else { aesKeysetHandle = KeysetHandle.generateNew( AeadKeyTemplates.AES256_GCM); logger.info("generate aes key file {}", aes); CleartextKeysetHandle.write(aesKeysetHandle, JsonKeysetWriter.withFile(aes)); } final var mac = new File("mac.json"); if (mac.exists()) { logger.info("load mac key from {}", mac); macKeysetHandle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(mac)); } else { macKeysetHandle = KeysetHandle.generateNew( MacKeyTemplates.HMAC_SHA256_256BITTAG); logger.info("generate mac key file {}", mac); CleartextKeysetHandle.write(macKeysetHandle, JsonKeysetWriter.withFile(mac)); } } private KeysetHandle aesKeysetHandle; private KeysetHandle macKeysetHandle; private Random random; }

2、第三方資料庫資訊類

public class Fwqsz implements Serializable {

    @Override
    public String toString() {
        return "(" + user + "@" + host + "/" + sid + ")";
    }

    public HikariDataSource open(String password) {
        return open(user, password);
    }

    public HikariDataSource open(String user, String password) {
        final var url = url();
        logger.info("連線 {}@{}", user, url);
        var ds = new HikariDataSource();
        ds.setJdbcUrl(url);
        ds.setUsername(user);
        ds.setPassword(password);
        return ds;
    }

    public String url() {
        return String.format("jdbc:oracle:thin:@%s:1521:%s", host, sid);
    }

    public void test(String password) throws SQLException {
        try (
                var conn = DriverManager.getConnection(url(), user, password);
                var stmt = conn.createStatement();
                var rs = stmt.executeQuery("select CURRENT_TIMESTAMP from dual")) {
            if (rs.next()) {
                logger.info("test oracle server: timestamp {}", rs.getString(1));
            }
        }
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column(length = 255, nullable = false)
    private String host;
    @Column(name = "`user`", length = 20, nullable = false)
    private String user;
    @Lob
    @JsonIgnore
    @Column(nullable = false)
    private byte[] salt;
    @Lob
    @JsonIgnore
    @Column(name = "`password`", nullable = false)
    private byte[] password;
    @Column(length = 20, nullable = false)
    private String sid;
    @Nationalized
    @Column(length = 100)
    private String yt;
    @Type(type = "yes_no")
    @Column(nullable = false)
    private boolean enable;

    @Version
    private int version;
    private Date updatedAt;
    private Date createdAt;

//使用時需要實現get、set方法
}

資料庫建表時,salt和password的型別為type="blob"

新增一條第三方資料庫資訊時,密碼需要加密

@Transactional(rollbackFor = Exception.class)
    @Override
    public void add(CreateFwqsz form) throws GeneralSecurityException {
        Fwqsz it = new Fwqsz();
        var now = new Date();
        it.setHost(form.getHost());
        it.setUser(form.getUser());
        it.setSalt(secretHelper.random(32));
        it.setPassword(secretHelper.encrypt(form.getPassword().getBytes(), it.getSalt()));
        it.setSid(form.getSid());
        it.setYt(form.getYt());
        it.setEnable(form.getEnable());
        it.setUpdatedAt(now);
        it.setCreatedAt(now);
        fwqszRepository.save(it);
    }

3、訪問第三方資料庫。先對密碼進行解密,new Object[]{}裡面的引數根據前面sql語句?的順序傳入

try (var ds = fwqsz.open(new String(secretHelper.decrypt(fwqsz.getPassword(), fwqsz.getSalt())))) {
                var open = new JdbcTemplate(ds);
                List<YecxKm> yecxKmList = = open.query("SELECT SERVERIP,SACC_CODE_C,IYEAR,SCORPCODE,SACC_NAME_C,ILEVEL,NOPEN_BAL FROM SUBJECT_C WHERE SERVERIP=? AND SCORPCODE=? AND IYEAR=? AND (SACC_CODE_C=? OR SACC_CODE_C LIKE ? ) ", new Object[]{corptype, scorpcode, year, saccCodeC, saccCodeC + "-%"}, new QjyecxMapper());
            } catch (GeneralSecurityException e) {
                logger.error("{}", e.getMessage());
                throw e;
            }

注意:

因為連結是 ds 負責管理的,要麼做成單例 給所有的beans用,要麼用完就釋放。把ds放try()內(用完之後自動關閉資料庫連線,上面例子為try語法),否則放finally內close。

4、建一個QjyecxMapper類

public class QjyecxMapper implements RowMapper<YecxKm> {
    @Override
    public YecxKm mapRow(ResultSet rs, int rowNum) throws SQLException {
        var it = new YecxKm();
        it.setServerip(rs.getString("SERVERIP"));
        it.setScorpcode(rs.getString("SCORPCODE"));
        it.setSaccCodeC(rs.getString("SACC_CODE_C"));
        it.setSaccNameC(rs.getString("SACC_NAME_C"));
        it.setIyear(rs.getString("IYEAR"));
        it.setIlevel(rs.getString("ILEVEL"));
        it.setNopenBal(rs.getString("NOPEN_BAL"));
        return it;
    }
}

二、JdbcTemplate的in語法引數解決辦法

上面的JdbcTemplate解決不了in語法,得使用NamedParameterJdbcTemplate

官網文件例子連線:https://www.technicalkeeda.com/spring-tutorials/spring-jdbctemplate-in-clause-example

例子:

var jdbcTemplate = new NamedParameterJdbcTemplate(ds);
HashMap<String, Object> params = new HashMap<>();
params.put("corptype", corptype);
params.put("scorpcode", scorpcode);
params.put("iyear", iyear);
params.put("sdocids", list);
List<Pzmxzb> pzmxzbList=open.query("SELECT SDOC_ID,SDOC_NO,SDOC_TYPE,SENTRIEDBY,SPERIODCODE,DDOC_DATE FROM GL_DOC_M WHERE SERVERIP=:corptype AND SCORPCODE=:scorpcode AND IYEAR=:iyear AND SDOC_ID IN (:sdocids)", params, new PzmxzbMapper());
list為集合,比如List<String>字串集合

注意:oracle的in語法的集合長度不能超過1000,否則會報錯,其實超過200就不合理了