JAVA与关系形数据库

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");
      }

JNDI

JNDI和JDBC都是JDK提供的SPI接口。但同时,JDNI又是J2EE规范(意味这tomcat等这一套j2ee规范的实现组建都支持JNDI)。JNDI理论上的访问不限于数据库。JNDI访问数据库也需要JDBC驱动,所以说JNDI是JDBC的上一个层次也没错。

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

 
  
    
    









  

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



    
    
    WEB-INF/web.xml
    ${catalina.base}/conf/web.xml
    
    





最后就是实际使用了

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) { }  
}
}

ORM

常用的的比如说Hibernate和mybaitis。ORM框架还一般会整合连接池。常见的连接池有dbcp(tomcat自带),c3p0,druid等。

Spring

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

整个代码的框架如下
JAVA与关系形数据库_第2张图片

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();

}

JTA

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(); 
        } 
    }

你可能感兴趣的:(java,数据库)