JAVA与关系形数据库

38 篇文章 0 订阅

JDBC

JDBC是JDK提供的JAVA访问关系型数据库的SPI,有多种实现方式。

ODBC是微软提出的!O是open不是oracle。也是最造被开发者和数据库厂商接收的交互方案。所以可以在ODBC的基础上构建JDBC,称为JDBC-ODBC Bridge Driver。ODBC通过调用数据库的本地客户端的API和远程数据库通信交互。

当然,可以直接调用数据库本地的客户端。也就是Native API Driver。不过,JDBC实现者就无法做到硬件无关了。所以,不被推荐。

JDBC-Net Driver,Java将sql翻译为数据库无关的操作协议,发给中间服务器,中间服务器负责翻译为具体服务器的操作转发到数据库服务器。ADO.NET是使用这种方案的。

Native Protocol Driver 最常用的一种,JDBC实现者直接将命令转化为操作数据的网络请求,直接发送给数据库。mysql-connector-java就属于这一种。

JDBC对数据库操作基本如下

      //加载驱动
      Class.forName("com.mysql.jdbc.Driver");
      System.out.println("Connecting to database...");
      //建立连接
      conn = DriverManager.getConnection(DB_URL,USER,PASS);

      //执行SQL
      System.out.println("Creating statement...");
      stmt = conn.createStatement();
      String sql;
      sql = "SELECT id, first, last, age FROM Employees";
      ResultSet rs = stmt.executeQuery(sql);

      //分析结果
      while(rs.next()){
         //Retrieve by column name
         int id  = rs.getInt("id");
         int age = rs.getInt("age");
      }

ORM框架

常用的的比如说Hibernate和mybaitis。ORM框架还一般会整合连接池。常见的连接池有dbcp(tomcat自带),c3p0,druid等。
程序员写SQL毕竟不开心。所以还是希望用java方法的方式来操作数据。

在这里插入图片描述

JPA

JPA是一个Java EE标准。当时Sun公司希望通过JPA整合ORM领域。不过后来没遵守JPA的mybatis反而流行了起来是大家没想到的。也算是JPA VS mybatis,也算是JAVA届的一个引战之道了。

JPA需要provider来实现其功能,就比如说Hibernate。

JNDI

JNDI和JDBC都是JDK提供的SPI接口。但同时,JDNI又是J2EE规范(意味这tomcat等这一套j2ee规范的实现组建都支持JNDI)。JNDI理论上的访问不限于数据库。JNDI访问数据库也需要JDBC驱动,所以说JNDI是JDBC的上一个层次也没错。JNDI是给类似运行在tomcat这样的J2EE的容器中的用户代码使用的。 当然用户可可以不用这个JNDI,自己管理数据库连接。

JNDI可以配置在诸如Tomcat这样的容器中。提供程序员数据访问,而开发者不需要在代码里关系数据访问的细节。以tomcat为例,server.xml配置文件如下,配置两个数据源

 <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
<Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />


<Resource auth="Container" 
             description="DB Connection" 
             driverClass="oracle.jdbc.driver.OracleDriver" 
             maxPoolSize="20" 
             minPoolSize="5" 
             acquireIncrement="5" 
             maxIdleTime="300"
             idleConnectionTestPeriod="60"
             automaticTestTable="Test"
             acquireRetryAttempts="30"
             breakAfterAcquireFailure="true"
             name="jdbc/amodMonDbSource"
             user="amod" 
             password="amoddb" 
             factory="org.apache.naming.factory.BeanFactory" 
             type="com.mchange.v2.c3p0.ComboPooledDataSource" 
             jdbcUrl="jdbc:oracle:thin:@10.111.0.108:1521:orcl" />


<Resource auth="Container" 
             description="DB Connection" 
             driverClass="oracle.jdbc.driver.OracleDriver" 
             maxPoolSize="20" 
             minPoolSize="5" 
             acquireIncrement="5" 
             maxIdleTime="300"
             idleConnectionTestPeriod="60"
             automaticTestTable="Test"
             acquireRetryAttempts="30"
             breakAfterAcquireFailure="true"
             name="jdbc/ddamodMonDbSource"
             user="ddamod" 
             password="ddamoddb" 
             factory="org.apache.naming.factory.BeanFactory" 
             type="com.mchange.v2.c3p0.ComboPooledDataSource" 
             jdbcUrl="jdbc:oracle:thin:@10.111.0.108:1521:orcl" />


  </GlobalNamingResources>

然后还需要在context.xml中进行配置

<!-- The contents of this file will be loaded for each web application -->
<Context>
    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->


<ResourceLink name="jdbc/amodMonDbSource" global="jdbc/amodMonDbSource" type="javax.sql.DataSource"/>
<ResourceLink name="jdbc/ddamodMonDbSource" global="jdbc/ddamodMonDbSource" type="javax.sql.DataSource"/>
</Context>

最后就是实际使用了

Connection conn=null;try {  
Context ctx=new InitialContext();  
Object datasourceRef=ctx.lookup("java:MySqlDS"); 
......  
c.close();
} catch(Exception e) {  
e.printStackTrace();
} finally {  
if(conn!=null) {    
try {      
conn.close();    
} catch(SQLException e) { }  
}
}

JTA

同样是一个JavaEE的规范。

Spring & SpringJDBC

事务乍一看和Spring没有什么关系。但是,至少在面试中,事务被称为Spring的核心之一也没有错。这关系到你能不能稳定的写出业务代码。
具体使用请参考
https://blog.csdn.net/define_us/article/details/52261978
这里只简单论述原理。
Spring事务其实就是Spring AOP,底层创建动态代理对象,在代码的开头结尾封装了开启事务和事务回滚操作。
在这里插入图片描述

整个代码的框架如下
在这里插入图片描述

public interface PlatformTransactionManager {
     // 获取事务状态信息
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
     // 提交事务
    void commit(TransactionStatus status) throws TransactionException;
     // 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

其实,有transaction manager就可以自己实现事务了

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
  userMapper.insertUser(user);
}
catch (MyException ex) {
  txManager.rollback(status);
  throw ex;
}
txManager.commit(status);

一个事务的定义如下

public interface TransactionDefinition {

    // 事务的传播行为
    int PROPAGATION_REQUIRED = 0;   
    int PROPAGATION_SUPPORTS = 1;   
    int PROPAGATION_MANDATORY = 2;  
    int PROPAGATION_REQUIRES_NEW = 3;   
    int PROPAGATION_NOT_SUPPORTED = 4;  
    int PROPAGATION_NEVER = 5;  
    int PROPAGATION_NESTED = 6;
    // 事务的隔离级别
    int ISOLATION_DEFAULT = -1; 
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
    // 事务的超时时间  
    int TIMEOUT_DEFAULT = -1;

    // 获取事务的传播行为
    int getPropagationBehavior();

    // 获取事务的隔离级别
    int getIsolationLevel();

    // 获取事务的超时时间
    int getTimeout();

    // 获取事务是否只读
    boolean isReadOnly();

    // 获取事务对象名称
    @Nullable
    String getName();

}

事务状态如下

public interface TransactionStatus extends SavepointManager, Flushable {

    // 是否是新事务
    boolean isNewTransaction();

    // 是否存在保存点
    boolean hasSavepoint();

    // 设置事务回滚
    void setRollbackOnly();

    // 是否回滚
    boolean isRollbackOnly();

    // 刷新事务
    @Override
    void flush();

    // 获取事务是否完成
    boolean isCompleted();

}

Spring的分库很简单,继承AbstractRoutingDataSource重写determineCurrentLookupKey()方法。就可以实现跨库了。但是实现跨库事务就没有这么简单了。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }
}

上面说了这么多,很遗憾都是单库事务。跨库(数据源)事务,包含一些消息队列的事务可以使用JTA来实现。JTA由以下三部分组成

  • 高层应用事务界定接口,供事务客户界定事务边界的
  • X/Open XA协议(资源之间的一种标准化的接口)的标准Java映射,它可以使事务性的资源管理器参与由外部事务管理器控制的事务中
  • 高层事务管理器接口,允许应用程序服务器为其管理的应用程序界定事务的边界
    主要包含以下接口
    JTA的主要接口 
    位于javax.transaction包中
    a、UserTransaction接口:让应用程序得以控制事务的开始、挂起、提交、回滚等。由Java客户端程序或EJB调用。
    b、TransactionManager 接口:用于应用服务器管理事务状态
    c、Transaction接口:用于执行相关事务操作
    d、XAResource接口:用于在分布式事务环境下,协调事务管理器和资源管理器的工作
    e、Xid接口:为事务标识符的Java映射

前3个接口位于Java EE版的类库 javaee.jar 中,JDK中没有提供。一个典型的JTA代码如下

public void transferAccount() { 
        
        UserTransaction userTx = null; 
        Connection connA = null; 
        Statement stmtA = null; 
                
        Connection connB = null; 
        Statement stmtB = null; 
    
        try{ 
              // 获得 Transaction 管理对象
            userTx = (UserTransaction)getContext().lookup("\
                  java:comp/UserTransaction"); 
            // 从数据库 A 中取得数据库连接
            connA = getDataSourceA().getConnection(); 
            
            // 从数据库 B 中取得数据库连接
            connB = getDataSourceB().getConnection(); 
      
                       // 启动事务
            userTx.begin();
            
            // 将 A 账户中的金额减少 500 
            stmtA = connA.createStatement(); 
            stmtA.execute("
           update t_account set amount = amount - 500 where account_id = 'A'");
            
            // 将 B 账户中的金额增加 500 
            stmtB = connB.createStatement(); 
            stmtB.execute("\
            update t_account set amount = amount + 500 where account_id = 'B'");
            
            // 提交事务
            userTx.commit();
            // 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新)
        } catch(SQLException sqle){ 
            
            try{ 
                  // 发生异常,回滚在本事务中的操纵
                 userTx.rollback();
                // 事务回滚:转账的两步操作完全撤销 
                //( 数据库 A 和数据库 B 中的数据更新被同时撤销)
                
                stmt.close(); 
                conn.close(); 
                ... 
            }catch(Exception ignore){ 
                
            } 
            sqle.printStackTrace(); 
            
        } catch(Exception ne){ 
            e.printStackTrace(); 
        } 
    }

连接池

https://blog.csdn.net/define_us/article/details/80625721

分库分表中间件

国内用的比较多的是当当的sharding-jdbc.
大众点评的zebra也是一个。

myBatis

如果说JPA是面向对象的数据库操作,那么mybatis就是面向关系的。虽然不方便,自动化差一点,但是sql便携则更灵活。

SpringData

简化了数据库访问,SpringData也可以使用JPA的底层(也就是Spring Data JPA)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值