欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis操作多數(shù)據(jù)源的實現(xiàn)

 更新時間:2023年05月15日 09:21:05   作者:Glimmer  
本文主要介紹了Mybatis操作多數(shù)據(jù)源,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

現(xiàn)在有一個Mysql數(shù)據(jù)源和一個Postgresql數(shù)據(jù)源,使用Mybatis對兩個數(shù)據(jù)源進(jìn)行操作:

1. 注入多數(shù)據(jù)源

可以對兩個數(shù)據(jù)源分別實現(xiàn)其Service層和Mapper層,以及Mybatis的配置類:

@Configuration
// 這里需要配置掃描包路徑,以及sqlSessionTemplateRef
@MapperScan(basePackages = "com.example.mybatisdemo.mapper.mysql", sqlSessionTemplateRef = "mysqlSqlSessionTemplate")
public class MysqlMybatisConfigurer {
? ? /**
? ? ?* 注入Mysql數(shù)據(jù)源
? ? ?*/
? ? @Bean
? ? @ConfigurationProperties(prefix = "spring.datasource.mysql")
? ? public DataSource mysqlDatasource() {
? ? ? ? return new DruidDataSource();
? ? }
? ? /**
? ? ?* 注入mysqlSqlSessionFactory
? ? ?*/
? ? @Bean
? ? public SqlSessionFactory mysqlSqlSessionFactory(DataSource mysqlDatasource) throws Exception {
? ? ? ? SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
? ? ? ? factoryBean.setDataSource(mysqlDatasource);
? ? ? ? // 設(shè)置對應(yīng)的mapper文件
? ? ? ? factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:" +
? ? ? ? ? ? ? ? "/mappers/MysqlMapper.xml"));
? ? ? ? return factoryBean.getObject();
? ? }
? ? /**
? ? ?* 注入mysqlSqlSessionTemplate
? ? ?*/
? ? @Bean
? ? public SqlSessionTemplate mysqlSqlSessionTemplate(SqlSessionFactory mysqlSqlSessionFactory) {
? ? ? ? return new SqlSessionTemplate(mysqlSqlSessionFactory);
? ? }
? ? /**
? ? ?* 注入mysqlTransactionalManager
? ? ?*/
? ? @Bean
? ? public DataSourceTransactionManager mysqlTransactionalManager(DataSource mysqlDatasource) {
? ? ? ? return new DataSourceTransactionManager(mysqlDatasource);
? ? }
}
@Configuration
// 這里需要配置掃描包路徑,以及sqlSessionTemplateRef
@MapperScan(basePackages = "com.example.mybatisdemo.mapper.postgresql", sqlSessionTemplateRef = "postgresqlSqlSessionTemplate")
public class PostgresqlMybatisConfigurer {
? ? /**
? ? ?* 注入Postgresql數(shù)據(jù)源
? ? ?*/
? ? @Bean
? ? @ConfigurationProperties(prefix = "spring.datasource.postgresql")
? ? public DataSource postgresqlDatasource() {
? ? ? ? return new DruidDataSource();
? ? }
? ? /**
? ? ?* 注入postgresqlSqlSessionFactory
? ? ?*/
? ? @Bean
? ? public SqlSessionFactory postgresqlSqlSessionFactory(DataSource postgresqlDatasource) throws Exception {
? ? ? ? SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
? ? ? ? factoryBean.setDataSource(postgresqlDatasource);
? ? ? ? // 設(shè)置對應(yīng)的mapper文件
? ? ? ? factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:" +
? ? ? ? ? ? ? ? "/mappers/PostgresqlMapper.xml"));
? ? ? ? return factoryBean.getObject();
? ? }
? ? /**
? ? ?* 注入postgresqlSqlSessionTemplate
? ? ?*/
? ? @Bean
? ? public SqlSessionTemplate postgresqlSqlSessionTemplate(SqlSessionFactory postgresqlSqlSessionFactory) {
? ? ? ? return new SqlSessionTemplate(postgresqlSqlSessionFactory);
? ? }
? ? /**
? ? ?* 注入postgresqlTransactionalManager
? ? ?*/
? ? @Bean
? ? public DataSourceTransactionManager postgresqlTransactionalManager(DataSource postgresqlDatasource) {
? ? ? ? return new DataSourceTransactionManager(postgresqlDatasource);
? ? }
}

在配置類中,分別注入了一個事務(wù)管理器TransactionManager,這個和事務(wù)管理是相關(guān)的。在使用@Transactional注解時,需要配置其value屬性指定對應(yīng)的事務(wù)管理器。

2. 動態(tài)數(shù)據(jù)源

Spring中提供了AbstractRoutingDataSource抽象類,可以用于動態(tài)地選擇數(shù)據(jù)源。

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
    @Nullable
    private Map<Object, Object> targetDataSources;
    @Nullable
    private Object defaultTargetDataSource;
    private boolean lenientFallback = true;
    private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
    @Nullable
    private Map<Object, DataSource> resolvedDataSources;
    @Nullable
    private DataSource resolvedDefaultDataSource;
    // 略
}

通過源碼可以看到,該抽象類實現(xiàn)了InitializingBean接口,并在其afterPropertiesSet方法中將數(shù)據(jù)源以<lookupkey, dataSource>的形式放入一個Map中。

public void afterPropertiesSet() {
? ? if (this.targetDataSources == null) {
? ? ? ? throw new IllegalArgumentException("Property 'targetDataSources' is required");
? ? } else {
? ? ? ? this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size());
? ? ? ? this.targetDataSources.forEach((key, value) -> {
? ? ? ? ? ? Object lookupKey = this.resolveSpecifiedLookupKey(key);
? ? ? ? ? ? DataSource dataSource = this.resolveSpecifiedDataSource(value);
? ? ? ? ? ? // 將數(shù)據(jù)源以<lookupkey, dataSource>的形式放入Map中
? ? ? ? ? ? this.resolvedDataSources.put(lookupKey, dataSource);
? ? ? ? });
? ? ? ? if (this.defaultTargetDataSource != null) {
? ? ? ? ? ? this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
? ? ? ? }
? ? }
}

該類中還有一個determineTargetDataSource方法,是根據(jù)lookupkey從Map中獲取對應(yīng)的數(shù)據(jù)源,如果沒有獲取到,則使用默認(rèn)的數(shù)據(jù)源。

protected DataSource determineTargetDataSource() {
? ? Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
? ? Object lookupKey = this.determineCurrentLookupKey();
? ? // 根據(jù)lookupkey從Map中獲取對應(yīng)的數(shù)據(jù)源
? ? DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
? ? if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
? ? ? ? dataSource = this.resolvedDefaultDataSource;
? ? }
? ? if (dataSource == null) {
? ? ? ? throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
? ? } else {
? ? ? ? return dataSource;
? ? }
}

lookupkey是通過determineTargetDataSource方法獲取到的,而它是一個抽象方法,我們要做的就是通過實現(xiàn)這個方法,來控制獲取到的數(shù)據(jù)源。

@Nullable
protected abstract Object determineCurrentLookupKey();

(1) 創(chuàng)建并注入動態(tài)數(shù)據(jù)源

創(chuàng)建AbstractRoutingDataSource的子類,實現(xiàn)determineCurrentLookupKey方法

public class RoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.get();
    }
}

這里的DataSourceContextHolder是一個操作ThreadLocal對象的工具類

public class DataSourceContextHolder {
? ? /**
? ? ?* 數(shù)據(jù)源上下文
? ? ?*/
? ? private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
? ? /**
? ? ?* 設(shè)置數(shù)據(jù)源類型
? ? ?*/
? ? public static void set(DataSourceType type) {
? ? ? ? contextHolder.set(type);
? ? }
? ? /**
? ? ?* 獲取數(shù)據(jù)源類型
? ? ?*
? ? ?* @return DataSourceType
? ? ?*/
? ? public static DataSourceType get() {
? ? ? ? return contextHolder.get();
? ? }
? ? /**
? ? ?* 使用MYSQL數(shù)據(jù)源
? ? ?*/
? ? public static void mysql() {
? ? ? ? set(DataSourceType.MYSQL);
? ? }
? ? /**
? ? ?* 使用Postgresql數(shù)據(jù)源
? ? ?*/
? ? public static void postgresql() {
? ? ? ? set(DataSourceType.POSTGRESQL);
? ? }
? ? public static void remove() {
? ? ? ? contextHolder.remove();
? ? }
}

通過調(diào)用DataSourceContextHolder.mysql()或者DataSourceContextHolder.postgresql()就能修改contextHolder的值,從而在動態(tài)數(shù)據(jù)源的determineTargetDataSource方法中就能獲取到對應(yīng)的數(shù)據(jù)源。

在數(shù)據(jù)源配置類中,將mysql和postgresql的數(shù)據(jù)源設(shè)置到動態(tài)數(shù)據(jù)源的Map中,并注入容器。

@Configuration
public class DataSourceConfigurer {
? ? @Bean
? ? @ConfigurationProperties(prefix = "spring.datasource.mysql")
? ? public DataSource mysqlDatasource() {
? ? ? ? return new DruidDataSource();
? ? }
? ? @Bean
? ? @ConfigurationProperties(prefix = "spring.datasource.postgresql")
? ? public DataSource postgresqlDatasource() {
? ? ? ? return new DruidDataSource();
? ? }
? ? @Bean
? ? public RoutingDataSource routingDataSource(DataSource mysqlDatasource, DataSource postgresqlDatasource) {
? ? ? ? Map<Object, Object> dataSources = new HashMap<>();
? ? ? ? dataSources.put(DataSourceType.MYSQL, mysqlDatasource);
? ? ? ? dataSources.put(DataSourceType.POSTGRESQL, postgresqlDatasource);
? ? ? ? RoutingDataSource routingDataSource = new RoutingDataSource();
? ? ? ? routingDataSource.setDefaultTargetDataSource(mysqlDatasource);
? ? ? ? // 設(shè)置數(shù)據(jù)源
? ? ? ? routingDataSource.setTargetDataSources(dataSources);
? ? ? ? return routingDataSource;
? ? }
}

(2) Mybatis配置類

由于使用了動態(tài)數(shù)據(jù)源,所以只需要編寫一個配置類即可。

@Configuration
@MapperScan(basePackages = "com.example.mybatisdemo.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
public class MybatisConfigurer {
? ? // 注入動態(tài)數(shù)據(jù)源
? ? @Resource
? ? private RoutingDataSource routingDataSource;
? ? @Bean
? ? public SqlSessionFactory sqlSessionFactory() throws Exception {
? ? ? ? SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
? ? ? ? sqlSessionFactoryBean.setDataSource(routingDataSource);
? ? ? ? // 這里可以直接設(shè)置所有的mapper.xml文件
? ? ? ? sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath" +
? ? ? ? ? ? ? ? ":mappers/*.xml"));
? ? ? ? return sqlSessionFactoryBean.getObject();
? ? }
? ? @Bean
? ? public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
? ? ? ? return new SqlSessionTemplate(sqlSessionFactory);
? ? }
? ? @Bean
? ? public DataSourceTransactionManager transactionalManager(DataSource mysqlDatasource) {
? ? ? ? return new DataSourceTransactionManager(mysqlDatasource);
? ? }
}

(3) 使用注解簡化數(shù)據(jù)源切換

我們雖然可以使用DataSourceContextHolder類中的方法進(jìn)行動態(tài)數(shù)據(jù)源切換,但是這種方式有些繁瑣,不夠優(yōu)雅??梢钥紤]使用注解的形式簡化數(shù)據(jù)源切換。
我們先定義兩個注解,表示使用Mysql數(shù)據(jù)源或Postgresql數(shù)據(jù)源:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Mysql {
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Postgresql {
}

再定義一個切面,當(dāng)使用了注解時,會先調(diào)用切換數(shù)據(jù)源的方法,再執(zhí)行后續(xù)邏輯。

@Component
@Aspect
public class DataSourceAspect {
? ? @Pointcut("@within(com.example.mybatisdemo.aop.Mysql) || @annotation(com.example.mybatisdemo.aop.Mysql)")
? ? public void mysqlPointcut() {
? ? }
? ? @Pointcut("@within(com.example.mybatisdemo.aop.Postgresql) || @annotation(com.example.mybatisdemo.aop.Postgresql)")
? ? public void postgresqlPointcut() {
? ? }
? ? @Before("mysqlPointcut()")
? ? public void mysql() {
? ? ? ? DataSourceContextHolder.mysql();
? ? }
? ? @Before("postgresqlPointcut()")
? ? public void postgresql() {
? ? ? ? DataSourceContextHolder.postgresql();
? ? }
}

在使用動態(tài)數(shù)據(jù)源的事務(wù)操作時有兩個需要注意的問題:

問題一    同一個事務(wù)操作兩個數(shù)據(jù)源

Mybatis使用Executor執(zhí)行SQL時需要獲取連接,BaseExecutor類中的getConnection方法調(diào)用了SpringManagedTransaction中的getConnection方法,這里優(yōu)先從connection字段獲取連接,如果connection為空,才會調(diào)用openConnection方法,并把連接賦給connection字段。

也就是說,如果你使用的是同一個事務(wù)來操作兩個數(shù)據(jù)源,那拿到的都是同一個連接,會導(dǎo)致數(shù)據(jù)源切換失敗。

protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = this.transaction.getConnection();
    return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
}
public Connection getConnection() throws SQLException {
? ? if (this.connection == null) {
? ? ? ? this.openConnection();
? ? }
? ? return this.connection;
}
private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
    LOGGER.debug(() -> {
        return "JDBC Connection [" + this.connection + "] will" + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring";
    });
}

問題二      兩個獨立事務(wù)分別操作兩個數(shù)據(jù)源

(1) 在開啟事務(wù)的時候,DataSourceTransactionManager中的doBegin方法會先獲取Connection,并保存到ConnectionHolder中,將數(shù)據(jù)源和ConnectionHolder的對應(yīng)關(guān)系綁定到TransactionSynchronizationManager中。

protected void doBegin(Object transaction, TransactionDefinition definition) {
? ? DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
? ? Connection con = null;
? ? try {
? ? ? ? if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
? ? ? ? ? ? // 獲取連接
? ? ? ? ? ? Connection newCon = this.obtainDataSource().getConnection();
? ? ? ? ? ? if (this.logger.isDebugEnabled()) {
? ? ? ? ? ? ? ? this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
? ? ? ? ? ? }
? ? ? ? ? ? // 保存到ConnectionHolder中
? ? ? ? ? ? txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
? ? ? ? }
? ? ? ? txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
? ? ? ? // 從ConnectionHolder獲取連接
? ? ? ? con = txObject.getConnectionHolder().getConnection();
? ? ? ? // 略
? ? ? ? // 將數(shù)據(jù)源和ConnectionHolder的關(guān)系綁定到TransactionSynchronizationManager中
? ? ? ? if (txObject.isNewConnectionHolder()) {
? ? ? ? ? ? TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
? ? ? ? }
? ? ? ? ?// 略
}

(2) TransactionSynchronizationManager的bindResource方法將數(shù)據(jù)源和ConnectionHolder的對應(yīng)關(guān)系存入線程變量resources中。

public abstract class TransactionSynchronizationManager {
? ? // 線程變量
? ? private static final ThreadLocal<Map<Object, Object>> resources =
? ? ? ? ?new NamedThreadLocal<>("Transactional resources");
? ? // 略
? ? // 綁定數(shù)據(jù)源和ConnectionHolder的對應(yīng)關(guān)系
? ? public static void bindResource(Object key, Object value) throws IllegalStateException {
? ? ? ?Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
? ? ? ?Assert.notNull(value, "Value must not be null");
? ? ? ?Map<Object, Object> map = resources.get();
? ? ? ?// set ThreadLocal Map if none found
? ? ? ?if (map == null) {
? ? ? ? ? map = new HashMap<>();
? ? ? ? ? resources.set(map);
? ? ? ?}
? ? ? ?Object oldValue = map.put(actualKey, value);
? ? ? ?// Transparently suppress a ResourceHolder that was marked as void...
? ? ? ?if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
? ? ? ? ? oldValue = null;
? ? ? ?}
? ? ? ?if (oldValue != null) {
? ? ? ? ? throw new IllegalStateException(
? ? ? ? ? ? ? ? "Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
? ? ? ?}
? ? }
? ? // 略
}

(3) 上邊提到的openConnection方法,其實最終也是從TransactionSynchronizationManager的resources中獲取連接的

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
? ? Assert.notNull(dataSource, "No DataSource specified");
? ? // 獲取ConnectionHolder
? ? ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
? ? if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
? ? ? ? logger.debug("Fetching JDBC Connection from DataSource");
? ? ? ? Connection con = fetchConnection(dataSource);
? ? ? ? if (TransactionSynchronizationManager.isSynchronizationActive()) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ConnectionHolder holderToUse = conHolder;
? ? ? ? ? ? ? ? if (conHolder == null) {
? ? ? ? ? ? ? ? ? ? holderToUse = new ConnectionHolder(con);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? conHolder.setConnection(con);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? holderToUse.requested();
? ? ? ? ? ? ? ? TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
? ? ? ? ? ? ? ? holderToUse.setSynchronizedWithTransaction(true);
? ? ? ? ? ? ? ? if (holderToUse != conHolder) {
? ? ? ? ? ? ? ? ? ? TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (RuntimeException var4) {
? ? ? ? ? ? ? ? releaseConnection(con, dataSource);
? ? ? ? ? ? ? ? throw var4;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return con;
? ? } else {
? ? ? ? conHolder.requested();
? ? ? ? if (!conHolder.hasConnection()) {
? ? ? ? ? ? logger.debug("Fetching resumed JDBC Connection from DataSource");
? ? ? ? ? ? conHolder.setConnection(fetchConnection(dataSource));
? ? ? ? }
? ? ? ? // 從ConnectionHolder中獲取連接
? ? ? ? return conHolder.getConnection();
? ? }
}

也就是說,如果修改了數(shù)據(jù)源,那么resources中就找不到對應(yīng)的連接,就可以重新獲取連接,從而達(dá)到切換數(shù)據(jù)源的目的。然而我們數(shù)據(jù)源的只有一個,就是動態(tài)數(shù)據(jù)源,因此即使使用兩個獨立事務(wù),也不能成功切換數(shù)據(jù)源。

3. 結(jié)語

如果想要使用動態(tài)數(shù)據(jù)源的事務(wù)處理,可能需要考慮使用多線程分布式的事務(wù)處理機(jī)制;
如果使用直接注入多個數(shù)據(jù)源的方式實現(xiàn)事務(wù)處理,實現(xiàn)簡單,但是各數(shù)據(jù)源事務(wù)是獨立的;
應(yīng)該根據(jù)具體情況進(jìn)行選擇。

到此這篇關(guān)于Mybatis操作多數(shù)據(jù)源的實現(xiàn)的文章就介紹到這了,更多相關(guān)Mybatis 多數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java去掉字符串最后一個逗號的方法

    Java去掉字符串最后一個逗號的方法

    Java中去掉字符串的最后一個逗號有多種實現(xiàn)方法,不同的方法適用于不同的場景,本文通過實例代碼介紹Java去掉字符串最后一個逗號的相關(guān)知識,感興趣的朋友一起看看吧
    2023-12-12
  • Spring中的之啟動過程obtainFreshBeanFactory詳解

    Spring中的之啟動過程obtainFreshBeanFactory詳解

    這篇文章主要介紹了Spring中的之啟動過程obtainFreshBeanFactory詳解,在refresh時,prepareRefresh后,馬上就調(diào)用了obtainFreshBeanFactory創(chuàng)建beanFactory以及掃描bean信息(beanDefinition),并通過BeanDefinitionRegistry注冊到容器中,需要的朋友可以參考下
    2024-02-02
  • java list用法示例詳解

    java list用法示例詳解

    java中可變數(shù)組的原理就是不斷的創(chuàng)建新的數(shù)組,將原數(shù)組加到新的數(shù)組中,下文對java list用法做了詳解
    2014-01-01
  • java實現(xiàn)適用于安卓的文件下載線程類

    java實現(xiàn)適用于安卓的文件下載線程類

    本文給大家分享的是java實現(xiàn)適用于安卓的文件下載線程類的代碼,有需要的小伙伴可以參考下
    2015-07-07
  • 詳解Java中接口的定義與實例代碼

    詳解Java中接口的定義與實例代碼

    這篇文章主要介紹了詳解Java中接口的定義與實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法

    struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法

    這篇文章主要給大家介紹了關(guān)于struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • Java?C++刷題leetcode1106解析布爾表達(dá)式

    Java?C++刷題leetcode1106解析布爾表達(dá)式

    這篇文章主要為大家介紹了Java?C++刷題leetcode1106解析布爾表達(dá)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Java的深拷貝與淺拷貝的幾種實現(xiàn)方式

    Java的深拷貝與淺拷貝的幾種實現(xiàn)方式

    這篇文章主要介紹了Java的深拷貝與淺拷貝的幾種實現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Java jdbc批量多線程讀取CVS文件入庫

    Java jdbc批量多線程讀取CVS文件入庫

    這篇文章主要為大家詳細(xì)介紹了Java jdbc批量多線程讀取CVS文件入庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 關(guān)于java數(shù)組與字符串相互轉(zhuǎn)換的問題

    關(guān)于java數(shù)組與字符串相互轉(zhuǎn)換的問題

    這篇文章主要介紹了java數(shù)組與字符串相互轉(zhuǎn)換的問題,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10

最新評論