一、通过数据库信息访问第三方数据库
本例子是从一张存储数据库信息表中查出第三方数据库信息,再进行访问的
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就不合理了