最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis插件+注解實(shí)現(xiàn)數(shù)據(jù)脫敏方式

 更新時間:2022年09月27日 14:53:25   作者:shenyang1026  
這篇文章主要介紹了Mybatis插件+注解實(shí)現(xiàn)數(shù)據(jù)脫敏方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

引入

問題

在項(xiàng)目中需要對用戶敏感數(shù)據(jù)進(jìn)行脫敏處理,例如身份證號、手機(jī)號等信息進(jìn)行加密再入庫。

解決思路

方法一:最簡單直接的方式就是對代碼中所有涉及到敏感數(shù)據(jù)的接口在查詢和插入時進(jìn)行加解密 

方法二:方法一會對代碼入侵很大,需要考慮到所有涉及到的接口,工作量極大,并且可能出現(xiàn)組員協(xié)作時沒有考慮到對數(shù)據(jù)加解密的問題。最后決定采用mybatis的插件在mybatis SQL執(zhí)行和查詢結(jié)果填充操作上進(jìn)行切入。上層業(yè)務(wù)調(diào)用不再需要考慮數(shù)據(jù)的加解密問題同時也保證了數(shù)據(jù)的脫敏

Mybatis插件原理

Mybatis的插件是通過攔截器實(shí)現(xiàn)的,Mabatis支持對四種對象進(jìn)行攔截

實(shí)現(xiàn)

  • 設(shè)置參數(shù)時對參數(shù)中含有敏感字段的數(shù)據(jù)進(jìn)行加密
  • 對查詢返回的結(jié)果進(jìn)行解密處理

基于上面兩種要求,我們只需要對ParameterHandler和ResultSetHandler進(jìn)行切入。

定義特定注解,在切入時只需要檢查字段中是否包含該注解來決定是否加解密

加解密注解

定義SensitiveData注解

import java.lang.annotation.*;
/**
 * 該注解定義在類上
 * 插件通過掃描類對象是否包含這個注解來決定是否繼續(xù)掃描其中的字段注解
 * 這個注解要配合EncryptTransaction注解
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:38
 **/
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
}

定義EncryptTransaction注解

import java.lang.annotation.*;
/**
 * 該注解有兩種使用方式
 * ①:配合@SensitiveData加在類中的字段上
 * ②:直接在Mapper中的方法參數(shù)上使用
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:40
 **/
@Documented
@Inherited
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptTransaction {
}

加解密工具類

解密接口

package sicnu.cs.ich.common.interceptor.transaction.service;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:43
 **/
public interface IDecryptUtil {
    /**
     * 解密
     *
     * @param result resultType的實(shí)例
     * @return T
     * @throws IllegalAccessException 字段不可訪問異常
     */
    <T> T decrypt(T result) throws IllegalAccessException;
}

加密接口

package sicnu.cs.ich.common.interceptor.transaction.service;
import java.lang.reflect.Field;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:41
 **/
public interface IEncryptUtil {
    /**
     * 加密
     *
     * @param declaredFields 加密字段
     * @param paramsObject   對象
     * @param <T>            入?yún)㈩愋?
     * @return 返回加密
     * @throws IllegalAccessException 不可訪問
     */
    <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;
}
package sicnu.cs.ich.common.interceptor.transaction.service.impl;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;
import java.lang.reflect.Field;
import java.util.Objects;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:43
 **/
@Component
public class DecryptImpl implements IDecryptUtil {
    /**
     * 解密
     *
     * @param result resultType的實(shí)例
     */
    @Override
    public <T> T decrypt(T result) throws IllegalAccessException {
        //取出resultType的類
        Class<?> resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //取出所有被DecryptTransaction注解的字段
            EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
            if (!Objects.isNull(encryptTransaction)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //對注解的字段進(jìn)行逐一解密
                    try {
                        field.set(result, DBAESUtil.decrypt(value));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return result;
    }
}

加密實(shí)現(xiàn)類

package sicnu.cs.ich.common.interceptor.transaction.service.impl;
import com.fasterxml.jackson.databind.ObjectReader;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:42
 **/
@Component
public class EncryptUtilImpl implements IEncryptUtil {
    @Override
    public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
            //取出所有被EncryptTransaction注解的字段
        for (Field field : declaredFields) {
            EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
            if (!Objects.isNull(encryptTransaction)) {
                field.setAccessible(true);
                Object object = field.get(paramsObject);
                //暫時只實(shí)現(xiàn)String類型的加密
                if (object instanceof String) {
                    String value = (String) object;
                    //加密
                    try {
                        field.set(paramsObject, DBAESUtil.encrypt(value));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return paramsObject;
    }
}

解密實(shí)現(xiàn)類

package sicnu.cs.ich.common.interceptor.transaction.service.impl;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;
import java.lang.reflect.Field;
import java.util.Objects;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:43
 **/
@Component
public class DecryptImpl implements IDecryptUtil {
    /**
     * 解密
     *
     * @param result resultType的實(shí)例
     */
    @Override
    public <T> T decrypt(T result) throws IllegalAccessException {
        //取出resultType的類
        Class<?> resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //取出所有被DecryptTransaction注解的字段
            EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
            if (!Objects.isNull(encryptTransaction)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //對注解的字段進(jìn)行逐一解密
                    try {
                        field.set(result, DBAESUtil.decrypt(value));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return result;
    }
}

加解密工具類

package sicnu.cs.ich.common.util.keyCryptor;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:45
 **/
public class DBAESUtil {
    private static final String DEFAULT_V = "6859505890402435";
    // 自己填寫
    private static final String KEY = "***";
    private static final String ALGORITHM = "AES";
    private static SecretKeySpec getKey() {
        byte[] arrBTmp = DBAESUtil.KEY.getBytes();
        // 創(chuàng)建一個空的16位字節(jié)數(shù)組(默認(rèn)值為0)
        byte[] arrB = new byte[16];
        for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }
        return new SecretKeySpec(arrB, ALGORITHM);
    }
    /**
     * 加密
     */
    public static String encrypt(String content) throws Exception {
        final Base64.Encoder encoder = Base64.getEncoder();
        SecretKeySpec keySpec = getKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return encoder.encodeToString(encrypted);
    }
    /**
     * 解密
     */
    public static String decrypt(String content) throws Exception {
        final Base64.Decoder decoder = Base64.getDecoder();
        SecretKeySpec keySpec = getKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        byte[] base64 = decoder.decode(content);
        byte[] original = cipher.doFinal(base64);
        return new String(original);
    }
}

插件實(shí)現(xiàn)

參數(shù)插件ParameterInterceptor

切入mybatis設(shè)置參數(shù)時對敏感數(shù)據(jù)進(jìn)行加密

Mybatis插件的使用就是通過實(shí)現(xiàn)Mybatis中的Interceptor接口

再配合@Intercepts注解

// 使用mybatis插件時需要定義簽名
// type標(biāo)識需要切入的Handler
// method表示要要切入的方法
@Intercepts({
@Signature(type = ParameterHandler.class, method = “setParameters”, args = PreparedStatement.class),
})
package sicnu.cs.ich.common.interceptor.transaction;
import com.baomidou.mybatisplus.core.MybatisParameterHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;
import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.PreparedStatement;
import java.util.*;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-22:48
 **/
@Slf4j
// 注入Spring
@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
public class ParameterInterceptor implements Interceptor {
    @Autowired
    private IEncryptUtil IEncryptUtil;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //@Signature 指定了 type= parameterHandler 后,這里的 invocation.getTarget() 便是parameterHandler
        //若指定ResultSetHandler ,這里則能強(qiáng)轉(zhuǎn)為ResultSetHandler
        MybatisParameterHandler parameterHandler = (MybatisParameterHandler) invocation.getTarget();
        // 獲取參數(shù)對像,即 mapper 中 paramsType 的實(shí)例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        //取出實(shí)例
        Object parameterObject = parameterField.get(parameterHandler);
        // 搜索該方法中是否有需要加密的普通字段
        List<String> paramNames = searchParamAnnotation(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass = parameterObject.getClass();
            //對類字段進(jìn)行加密
            //校驗(yàn)該實(shí)例的類是否被@SensitiveData所注解
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
            if (Objects.nonNull(sensitiveData)) {
                //取出當(dāng)前當(dāng)前類所有字段,傳入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                IEncryptUtil.encrypt(declaredFields, parameterObject);
            }
            // 對普通字段進(jìn)行加密
            if (!CollectionUtils.isEmpty(paramNames)) {
                // 反射獲取 BoundSql 對象,此對象包含生成的sql和sql的參數(shù)map映射
                Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");
                boundSqlField.setAccessible(true);
                BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);
                PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
                // 改寫參數(shù)
                processParam(parameterObject, paramNames);
                // 改寫的參數(shù)設(shè)置到原parameterHandler對象
                parameterField.set(parameterHandler, parameterObject);
                parameterHandler.setParameters(ps);
            }
        }
        return invocation.proceed();
    }
    private void processParam(Object parameterObject, List<String> params) throws Exception {
        // 處理參數(shù)對象  如果是 map 且map的key 中沒有 tenantId,添加到參數(shù)map中
        // 如果參數(shù)是bean,反射設(shè)置值
        if (parameterObject instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, String> map = ((Map<String, String>) parameterObject);
            for (String param : params) {
                String value = map.get(param);
                map.put(param, value==null?null:DBAESUtil.encrypt(value));
            }
//            parameterObject = map;
        }
    }
    private List<String> searchParamAnnotation(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
        Class<MybatisParameterHandler> handlerClass = MybatisParameterHandler.class;
        Field mappedStatementFiled = handlerClass.getDeclaredField("mappedStatement");
        mappedStatementFiled.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementFiled.get(parameterHandler);
        String methodName = mappedStatement.getId();
        Class<?> mapperClass = Class.forName(methodName.substring(0, methodName.lastIndexOf('.')));
        methodName = methodName.substring(methodName.lastIndexOf('.') + 1);
        Method[] methods = mapperClass.getDeclaredMethods();
        Method method = null;
        for (Method m : methods) {
            if (m.getName().equals(methodName)) {
                method = m;
                break;
            }
        }
        List<String> paramNames = null;
        if (method != null) {
            Annotation[][] pa = method.getParameterAnnotations();
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < pa.length; i++) {
                for (Annotation annotation : pa[i]) {
                    if (annotation instanceof EncryptTransaction) {
                        if (paramNames == null) {
                            paramNames = new ArrayList<>();
                        }
                        paramNames.add(parameters[i].getName());
                    }
                }
            }
        }
        return paramNames;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}

返回值插件ResultSetInterceptor

package sicnu.cs.ich.common.interceptor.transaction;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
/**
 * @author 沈洋 郵箱:1845973183@qq.com
 * @create 2021/10/26-23:02
 **/
@Slf4j
@Component
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetInterceptor implements Interceptor {
    @Autowired
    private sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil IDecryptUtil;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //取出查詢的結(jié)果
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        //基于selectList
        if (resultObject instanceof ArrayList) {
            @SuppressWarnings("unchecked")
            ArrayList<Objects> resultList = (ArrayList<Objects>) resultObject;
            if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                for (Object result : resultList) {
                    //逐一解密
                    IDecryptUtil.decrypt(result);
                }
            }
            //基于selectOne
        } else {
            if (needToDecrypt(resultObject)) {
                IDecryptUtil.decrypt(resultObject);
            }
        }
        return resultObject;
    }
    private boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
        return Objects.nonNull(sensitiveData);
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}

使用

注解在實(shí)體類上

import lombok.*;
import org.springframework.security.core.userdetails.UserDetails;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;
@With
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@SensitiveData // 插件只對加了該注解的類進(jìn)行掃描,只有加了這個注解的類才會生效
public class User implements Serializable {
    private Integer id;
    private String username;
    private String openId;
    private String password;
    // 表明對該字段進(jìn)行加密
    @EncryptTransaction
    private String email;
    // 表明對該字段進(jìn)行加密
    @EncryptTransaction
    private String mobile;
    private Date createTime;
    private Date expireTime;
    private Boolean status = true;
}

注解在參數(shù)上

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
@Mapper
public interface UserMapper extends BaseMapper<User> {
   // 只需要在參數(shù)前加上@EncryptTransaction 即可
   long countByEmail(@EncryptTransaction @Param("email") String email);
   long countByMobile(@EncryptTransaction @Param("mobile") String mobile);
}

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot啟動時運(yùn)行特定代碼的多種方式小結(jié)

    SpringBoot啟動時運(yùn)行特定代碼的多種方式小結(jié)

    SpringBoot提供了多種方式在應(yīng)用程序啟動時運(yùn)行特定的代碼,包括CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件機(jī)制和自定義注解等,下面就來具體介紹一下
    2025-01-01
  • java中同類對象之間的compareTo()和compare()方法對比分析

    java中同類對象之間的compareTo()和compare()方法對比分析

    這篇文章主要介紹了java中同類對象之間的compareTo()和compare()方法對比分析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 詳解Java如何在CompletableFuture中實(shí)現(xiàn)日志記錄

    詳解Java如何在CompletableFuture中實(shí)現(xiàn)日志記錄

    這篇文章主要為大家詳細(xì)介紹了一種slf4j自帶的MDC類,來記錄完整的請求日志,和在CompletableFuture異步線程中如何保留鏈路id,需要的可以參考一下
    2023-04-04
  • java實(shí)現(xiàn)隊(duì)列數(shù)據(jù)結(jié)構(gòu)代碼詳解

    java實(shí)現(xiàn)隊(duì)列數(shù)據(jù)結(jié)構(gòu)代碼詳解

    這篇文章主要介紹了java實(shí)現(xiàn)隊(duì)列數(shù)據(jù)結(jié)構(gòu)代碼詳解,簡單介紹了隊(duì)列結(jié)構(gòu)以應(yīng)用場景,涉及詳細(xì)實(shí)現(xiàn)代碼,還是比較不錯的,這里分享給大家,需要的朋友可以參考下。
    2017-11-11
  • java?9大性能優(yōu)化經(jīng)驗(yàn)總結(jié)

    java?9大性能優(yōu)化經(jīng)驗(yàn)總結(jié)

    這篇文章主要介紹了java?9大性能優(yōu)化經(jīng)驗(yàn)總結(jié),包括:Java代碼優(yōu)化,數(shù)據(jù)庫優(yōu)化,分布式緩存,異步化,Web前段,搜索引擎優(yōu)化等需要的朋友可以參考下
    2023-02-02
  • Java中for、while、do while三種循環(huán)語句的區(qū)別介紹

    Java中for、while、do while三種循環(huán)語句的區(qū)別介紹

    這篇文章主要介紹了Java中for、while、do while三種循環(huán)語句的區(qū)別介紹的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • Spring中依賴注入(DI)幾種方式解讀

    Spring中依賴注入(DI)幾種方式解讀

    這篇文章主要介紹了Spring中依賴注入(DI)幾種方式解讀,構(gòu)造器依賴注入通過容器觸發(fā)一個類的構(gòu)造器來實(shí)現(xiàn)的,該類有一系列參數(shù),每個參數(shù)代表一個對其他類的依賴,需要的朋友可以參考下
    2024-01-01
  • MyBatis中特殊符號的轉(zhuǎn)義

    MyBatis中特殊符號的轉(zhuǎn)義

    編寫SQL中會用到<,>,,>= 等,但是在mybatis中不可以這么寫,與xml文件的元素沖突,所以需要轉(zhuǎn)義,本文主要介紹了MyBatis中特殊符號的轉(zhuǎn)義,主要介紹了兩種轉(zhuǎn)義方式,感興趣的可以了解一下
    2024-01-01
  • mybatis之如何獲取表中某一列的最大值

    mybatis之如何獲取表中某一列的最大值

    這篇文章主要介紹了mybatis之如何獲取表中某一列的最大值問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 一文帶你快速學(xué)會JDBC及獲取連接的五種方式

    一文帶你快速學(xué)會JDBC及獲取連接的五種方式

    JDBC(Java Database Connectivity)是一個獨(dú)立于特定數(shù)據(jù)庫管理系統(tǒng)、通用的SQL數(shù)據(jù)庫存取和操作的公共接口,下面這篇文章主要給大家介紹了關(guān)于如何通過一文帶你快速學(xué)會JDBC及獲取連接的五種方式,需要的朋友可以參考下
    2022-09-09

最新評論

横峰县| 湖南省| 静海县| 博爱县| 石家庄市| 仁布县| 金湖县| 四会市| 中卫市| 太仓市| 浦县| 葫芦岛市| 商河县| 尚义县| 天气| 博客| 临颍县| 淅川县| 通州区| 九龙城区| 合阳县| 青河县| 聂拉木县| 南华县| 定日县| 珠海市| 绵阳市| 桑植县| 扬州市| 塘沽区| 洱源县| 富锦市| 宁阳县| 丰原市| 凤山县| 三明市| 中宁县| 图们市| 临洮县| 天长市| 武功县|