Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
+关注继续查看

一、缘由

上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法介绍到了怎么样在Sping、MyBatis、Hibernate整合的应用中动态切换DataSource数据源的方法,但最终遗留下一个问题:不能切换数据库方言。数据库方言可能在当前应用的架构中意义不是很大,但是如果单纯用MyBatis或Hibernate做数据库持久化操作,还是要处理这一问题。

 

那么下面将介绍怎么样动态切换SessionFactory,为什么要切换SessionFactory?

因为这里切换SessionFactory就可以实现多数据源和多个SessionFactory,每个SessionFactory有自己独立的数据库配置和SessionFactory的相关配置。我们的数据库方言就配置在SessionFactory这里,所以实现了切换SessionFactory也就实现了切换数据库方言的问题。这个主要是针对Hibernate来操作的,而MyBatis则需要动态切换SqlSessionFactory才行。

 

二、实现代码

1、定义全局切换SessionFactory的工具

package com.hoo.framework.spring.support;
 
/**
 * <b>function:</b> 多数据源
 * @author hoojo
 * @createDate 2013-9-27 上午11:36:57
 * @file CustomerContextHolder.java
 * @package com.hoo.framework.spring.support
 * @project SHMB
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public abstract class CustomerContextHolder {
 
    public final static String SESSION_FACTORY_MYSQL = "mysql";
    public final static String SESSION_FACTORY_ORACLE = "oracle";
    
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
    
    public static void setCustomerType(String customerType) {  
        contextHolder.set(customerType);  
    }  
      
    public static String getCustomerType() {  
        return contextHolder.get();  
    }  
      
    public static void clearCustomerType() {  
        contextHolder.remove();  
    }  
}

同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。

2、实现自己的SessionFactory

定义好接口

package com.hoo.framework.spring.support.core;
 
import org.hibernate.SessionFactory;
 
/**
 * <b>function:</b> 动态SessionFactory接口
 * @author hoojo
 * @createDate 2013-10-12 下午03:29:52
 * @file DynamicSessionFactory.java
 * @package com.hoo.framework.spring.support.core
 * @project SHMB
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public interface DynamicSessionFactory extends SessionFactory {
    
    public SessionFactory getHibernateSessionFactory();
}

 

实现接口

package com.hoo.framework.spring.support.core;
 
import java.io.Serializable;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.Reference;
import org.hibernate.Cache;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.TypeHelper;
import org.hibernate.classic.Session;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.stat.Statistics;
import com.hoo.framework.spring.support.CustomerContextHolder;
 
/**
 * <b>function:</b> 动态数据源实现
 * @author hoojo
 * @createDate 2013-10-12 下午03:31:31
 * @file DynamicSessionFactoryImpl.java
 * @package com.hoo.framework.spring.support.core
 * @project SHMB
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
@SuppressWarnings({ "unchecked", "deprecation" })
public class DynamicSessionFactoryImpl implements DynamicSessionFactory {
 
    private static final long serialVersionUID = 5384069312247414885L;
    
    private Map<Object, SessionFactory> targetSessionFactorys;  
    private SessionFactory defaultTargetSessionFactory; 
    
    /**
     * @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory()
     * <b>function:</b> 重写这个方法,这里最关键
     * @author hoojo
     * @createDate 2013-10-18 上午10:45:25
     */
    @Override
    public SessionFactory getHibernateSessionFactory() {
        SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());  
        if (targetSessionFactory != null) {  
            return targetSessionFactory;  
        } else if (defaultTargetSessionFactory != null) {  
            return defaultTargetSessionFactory;  
        }  
        return null;
    }
 
 
    @Override
    public void close() throws HibernateException {
        this.getHibernateSessionFactory().close();
    }
 
    @Override
    public boolean containsFetchProfileDefinition(String s) {
        return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);
    }
 
    @Override
    public void evict(Class clazz) throws HibernateException {
        this.getHibernateSessionFactory().evict(clazz);
    }
 
    @Override
    public void evict(Class clazz, Serializable serializable) throws HibernateException {
        this.getHibernateSessionFactory().evict(clazz, serializable);
    }
 
    @Override
    public void evictCollection(String s) throws HibernateException {
        this.getHibernateSessionFactory().evictCollection(s);
    }
 
    @Override
    public void evictCollection(String s, Serializable serializable) throws HibernateException {
        this.getHibernateSessionFactory().evictCollection(s, serializable);
    }
 
    @Override
    public void evictEntity(String entity) throws HibernateException {
        this.getHibernateSessionFactory().evictEntity(entity);
    }
 
    @Override
    public void evictEntity(String entity, Serializable serializable) throws HibernateException {
        this.getHibernateSessionFactory().evictEntity(entity, serializable);
    }
 
    @Override
    public void evictQueries() throws HibernateException {
        this.getHibernateSessionFactory().evictQueries();        
    }
 
    @Override
    public void evictQueries(String queries) throws HibernateException {
        this.getHibernateSessionFactory().evictQueries(queries);        
    }
 
    @Override
    public Map<String, ClassMetadata> getAllClassMetadata() {
        return this.getHibernateSessionFactory().getAllClassMetadata();
    }
 
    @Override
    public Map getAllCollectionMetadata() {
        return this.getHibernateSessionFactory().getAllClassMetadata();
    }
 
    @Override
    public Cache getCache() {
        return this.getHibernateSessionFactory().getCache();
    }
 
    @Override
    public ClassMetadata getClassMetadata(Class clazz) {
        return this.getHibernateSessionFactory().getClassMetadata(clazz);
    }
 
    @Override
    public ClassMetadata getClassMetadata(String classMetadata) {
        return this.getHibernateSessionFactory().getClassMetadata(classMetadata);
    }
 
    @Override
    public CollectionMetadata getCollectionMetadata(String collectionMetadata) {
        return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata);
    }
 
    @Override
    public Session getCurrentSession() throws HibernateException {
        return this.getHibernateSessionFactory().getCurrentSession();
    }
 
    @Override
    public Set getDefinedFilterNames() {
        return this.getHibernateSessionFactory().getDefinedFilterNames();
    }
 
    @Override
    public FilterDefinition getFilterDefinition(String definition) throws HibernateException {
        return this.getHibernateSessionFactory().getFilterDefinition(definition);
    }
 
    @Override
    public Statistics getStatistics() {
        return this.getHibernateSessionFactory().getStatistics();
    }
 
    @Override
    public TypeHelper getTypeHelper() {
        return this.getHibernateSessionFactory().getTypeHelper();
    }
 
    @Override
    public boolean isClosed() {
        return this.getHibernateSessionFactory().isClosed();
    }
 
    @Override
    public Session openSession() throws HibernateException {
        return this.getHibernateSessionFactory().openSession();
    }
 
    @Override
    public Session openSession(Interceptor interceptor) throws HibernateException {
        return this.getHibernateSessionFactory().openSession(interceptor);
    }
 
    @Override
    public Session openSession(Connection connection) {
        return this.getHibernateSessionFactory().openSession(connection);
    }
 
    @Override
    public Session openSession(Connection connection, Interceptor interceptor) {
        return this.getHibernateSessionFactory().openSession(connection, interceptor);
    }
 
    @Override
    public StatelessSession openStatelessSession() {
        return this.getHibernateSessionFactory().openStatelessSession();
    }
 
    @Override
    public StatelessSession openStatelessSession(Connection connection) {
        return this.getHibernateSessionFactory().openStatelessSession(connection);
    }
 
    @Override
    public Reference getReference() throws NamingException {
        return this.getHibernateSessionFactory().getReference();
    }
 
    public void setTargetSessionFactorys(Map<Object, SessionFactory> targetSessionFactorys) {
        this.targetSessionFactorys = targetSessionFactorys;
    }
 
    public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) {
        this.defaultTargetSessionFactory = defaultTargetSessionFactory;
    }
 
}

上面最重要的就是getHibernateSessionFactory重写这个方法,其他方法和原来实现的无异。重写这个方法后利用CustomerContextHolder动态设置SessionFactory类型就可以动态的切换SessionFactory。

3、动态的事务管理器,因为我们这里是动态切换SessionFactory,所以事务这块也需要动态切换SessionFactory来完成事务的操作。

package com.hoo.framework.spring.support.tx;
 
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import com.hoo.framework.spring.support.core.DynamicSessionFactory;
 
/**
 * <b>function:</b> 重写HibernateTransactionManager事务管理器,实现自己的动态的事务管理器
 * @author hoojo
 * @createDate 2013-10-12 下午03:54:02
 * @file DynamicTransactionManager.java
 * @package com.hoo.framework.spring.support.tx
 * @project SHMB
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class DynamicTransactionManager extends HibernateTransactionManager {
 
    private static final long serialVersionUID = -4655721479296819154L;
    
    /** 
     * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource()
     * <b>function:</b> 重写
     * @author hoojo
     * @createDate 2013-10-12 下午03:55:24
     */
    @Override
    public DataSource getDataSource() {
        return SessionFactoryUtils.getDataSource(getSessionFactory());
    }
 
    /** 
     * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory()
     * <b>function:</b> 重写
     * @author hoojo
     * @createDate 2013-10-12 下午03:55:24
     */
    @Override
    public SessionFactory getSessionFactory() {
        DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();  
        SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();  
        return hibernateSessionFactory;  
    }
}

这里主要重写getDataSource()/getSessionFactory()这两个方法,getSessionFactory方法是利用我们上面定义的接口来动态获取我们在上下文(CustomerContextHolder)中定义切换的SessionFactory对象。而getDataSource则是获得动态SessionFactory的DataSource,这里也不难理解。

4、至此,重写的接口和实现都完成,下面开始配置相关的代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
    http://www.springframework.org/schema/tx  
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
    
    <!-- 配置c3p0数据源 -->
    <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${datasource.driver}"/>
        <property name="jdbcUrl" value="${datasource.url}"/>
        <property name="user" value="${datasource.username}"/>
        <property name="password" value="${datasource.password}"/>
                
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
        <property name="maxStatements" value="${c3p0.maxStatements}"/>
        <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
        <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
        <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
    </bean>
    
    <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"/>
        <property name="user" value="root"/>
        <property name="password" value="jp2011"/>
                
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
        <property name="maxStatements" value="${c3p0.maxStatements}"/>
        <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
        <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
        <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
    </bean>
    
    <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->
    <bean id="mySQLSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSourceMySQL"/>
        <property name="packagesToScan" value="com.hoo.**.mysqlentity"/>
        <property name="annotatedClasses">
            <array>
                <value>com.hoo.common.entity.IDGenerator</value>
            </array>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <!-- 链接释放策略 on_close | after_transaction | after_statement | auto  -->
                <prop key="hibernate.connection.release_mode">after_transaction</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>
    
    <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->
    <bean id="oracleSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSourceOracle"/>
        <property name="packagesToScan" value="com.hoo.**.entity"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
                <prop key="hibernate.connection.release_mode">after_transaction</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <!--prop key="hibernate.hbm2ddl.auto">update</prop-->
            </props>
        </property>
    </bean>
    
    <!-- 动态SessionFactory -->
    <bean id="sessionFactory" class="com.hoo.framework.spring.support.core.DynamicSessionFactoryImpl">
        <property name="defaultTargetSessionFactory" ref="oracleSessionFactory"/>
        <property name="targetSessionFactorys">
            <map>     
                <entry value-ref="oracleSessionFactory" key="oracle"/>
                <entry value-ref="mySQLSessionFactory" key="mysql"/>
            </map> 
        </property>
    </bean>
 
    <!-- 自定义动态切换SessionFactory事务管理器,注入sessionFactory  -->
    <bean id="transactionManager" class="com.hoo.framework.spring.support.tx.DynamicTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <!-- 配置事务的传播特性 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="*" read-only="true" />
        </tx:attributes>
    </tx:advice>
    
    <!-- 配置那些类、方法纳入到事务的管理 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" />
    </aop:config>
</beans>

配置也和我们之前的配置差不多,就是事务管理器部分注入的SessionFactory是我们自己定义的。

5、简单测试

@Test
public void testAdd() {
    // 主要就是这行代码 它才是完成SessionFactory的切换
    CustomerContextHolder.setCustomerType(CustomerContextHolder.SESSION_FACTORY_MYSQL);
    
    DeviceInfo entity = new DeviceInfo();
    entity.setSbbh(System.currentTimeMillis() + "");
    entity.setIpdz("my ip address2");
    entity.setJd(1234);
    try {
        service.add(entity);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

经过测试发现可以查询数据,利用hibernate查询分页也可以生成对应数据库的分页语句。同样在服务层的方法中添加完操作后,特意抛出一个异常,数据也能正常回滚操作,所以DynamicTransactionManager也是起到了该有的作用!

这里我的测试用例是手动切换CustomerContextHolder.setCustomerType的,但实际开发中我们还是得用Spring的Aop中的Interceptor进行切面编程,完成动态切换SessionFactory。上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法已经提到了(读者可以参考该篇博文中的第二节的3、7小节DataSourceMethodInterceptor MultipleDataSourceInterceptor),这里就不再赘述!

 

三、引发的问题

上述的实现如果在使用不当的情况下,在实际开发中很可能存在一些问题!

问题1是这样的,通常事务是在Service这层完成,这个应该是没有异议。倘若是这样的话,问题便出现了。而通常我们在MVC中的视图层中仅仅会调用Service中一个方法来完成所有当前业务的处理。如果这个Service中同时操作dbA、dbB两个数据库,事务提交的时候是用哪个数据库的事务呢?所以我们把不同数据库的操作放在一个方法,就会出现事务的问题的,除非两个数据库的事务能够同时回滚!

大致情景是:Service中的add4Oracle操作Oracle数据库,Service中的add4MySQL操作MySQL数据库,最后把Service中的add4Oracle、add4MySQL方法放到一个operation方法中,MVC视图层的业务控制调用Service中的operation。像这样的情况,如果add4Oracle或add4MySQL方法中的某一个方法出现异常,就需要把两个数据库事务都回滚。

解决办法就是在Service或Dao中的一个方法中同时操作两个数据库,手动完成事务控制。出现异常就全部回滚,没有异常就全部提交,只能这样牺牲下。

问题2是利用拦截器不能同时切换一个方法操作两个数据库的可能,例如一个service中的query方法即需要用query MySQL,也需要query Oracle。那么解决办法就是在query方法中调用当前service的query4Oracle和query4Oracle,那样绕过去还是能利用拦截器进行动态切换的。

四、总结

要完成数据库方言的切换,我们就需要配置多个SessionFactory利用自己实现的DynamicSessionFactory和CustomerContextHolder完成SessionFactory的切换。利用DynamicTransactionManager完成当前Session的事务操作。通过对这些对象的操作和配置,最终可以完成SessionFactory的动态切换。实际使用中虽然有些问题出现,但是最终还是有解决方案,尽管有些不完美。所谓的完美的东西总有它不完美的地方,这样才算完美,比如断臂的维纳斯女神~!






本文转自hoojo博客园博客,原文链接:http://www.cnblogs.com/hoojo/p/dynamic_switch_sessionfactory_muliteSessionFactory.html,如需转载请自行联系原作者
相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
XML Java 数据库连接
SpringBoot和Mybatis配置多数据源连接多个数据库
SpringBoot和Mybatis配置多数据源连接多个数据库
173 0
|
4月前
|
XML Java 编译器
JdbcTemplate--spring 提供的一个操作数据库(表)功能强大的类
JdbcTemplate--spring 提供的一个操作数据库(表)功能强大的类
45 0
|
5月前
|
SQL XML Java
MyBatis—操作数据库(四)
MyBatis—操作数据库(四)
|
5月前
|
XML Java 关系型数据库
MyBatis—操作数据库(一)
MyBatis—操作数据库
|
7月前
|
XML SQL Java
【Spring】使用 MyBatis 操作数据库
1. MyBatis 是什么 2. 为什么要使用 MyBatis 3. MyBatis 框架交互流程 4. 配合 Spring 来使用 MyBatis 4.1 添加 MyBatis 框架支持 4.2 配置数据库 4.3 添加实体类 4.4 添加 mapper 接口 4.5 通过注解的方式操作数据库 4.5.1 增加用户 4.5.2 删除用户 4.5.3 修改用户数据 4.5.4 查询用户数据 4.6 通过 XML 文件的形式操作数据库 4.6.1 配置 XML 文件 4.6.2 在 yml 中配置 xml 路径 4.6.3 查询用户数据 4.6.4 批量查询用户数据 4.6.5 批量插入用户数
156 0
【Spring】使用 MyBatis 操作数据库
|
8月前
|
SQL Java 数据库
Spring基础篇:Spring操作数据库的方式以及事务控制(下)
Spring操作数据库的方式以及事务控制(下)
96 0
|
8月前
|
XML SQL Java
Spring基础篇:Spring操作数据库的方式以及事务控制(上)
Spring操作数据库的方式以及事务控制(上)
190 0
|
10月前
|
Java 数据库 Spring
Spring学习(十):JdbcTemplate操作数据库实现查询功能
Spring学习(十):JdbcTemplate操作数据库实现查询功能
156 0
Spring学习(十):JdbcTemplate操作数据库实现查询功能
|
10月前
|
SQL Java 数据库连接
Spring学习(九):JdbcTemplate操作数据库实现增加功能
Spring学习(九):JdbcTemplate操作数据库实现增加功能
Spring学习(九):JdbcTemplate操作数据库实现增加功能
|
XML Java 关系型数据库
mybatis学习笔记(3)—数据库和bean名称不一样处理方案
之前写的mybatis对象的bean对象和数据库的对象名称是对应的,然而再实际开发的过程有很多不一一对应的情况。就需要解决。
124 0
mybatis学习笔记(3)—数据库和bean名称不一样处理方案
推荐文章
更多