栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-JDBC-基础

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring-JDBC-基础

JDBC概述

JDBC(java Database Connectivity):java数据库连接。是一种用于执行sql语句的Java API。JDBC是Java访问数据库的标准规范。可以为不同的关系型数据库提供统一的访问,它由一组用Java语言编写的接口和类组成。

驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。

我们已经知道JDBC是访问数据库的规范。那么生产厂家提供规范的实现类称为驱动。

原生JDBC访问数据库

项目要使用数据库。首先要导入对应的依赖(驱动)。以mysql为例:

 
        mysql
        mysql-connector-java
        5.1.47
    
1.初始化驱动
Class.forName("com.mysql.jdbc,Driver");

目的就是把Driver.class文件加载到内存。
1.JVM会加载这个类

2.加载这个类后,进入com.mysql.jdbc.Driver,会执行里面的静态代码块:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }

将驱动类中的Driver注册到DriverManager类中。

3…进入java.sql.DriverManager,DriverManager管理器会调用注册方法,并把Driver放入registeredDrivers列表中:

private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList();

public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {

        
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

4.调用DriverManager.getConnection获取连接时:

 @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

5.接着看getConnection

 //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String url, java.util.Properties info, Class caller) throws SQLException {
        
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized (DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection("" + url + "")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }
}

核心逻辑就是:
  校验集合registeredDrivers 中的驱动是否存在。存在执行下面逻辑。
  从集合registeredDrivers 中取出DriverInfo(Driver的包装),通过调用 Connection con = aDriver.driver.connect(url, info);得到连接对象。也就是调用驱动Driver中的connect方法获取连接。

2.建立连接
Connection conn = DriverManager.getConnection(url,username,password)

根据数据库的连接地址,用户名,密码。与数据库建立连接。

3.获取SQL语句执行对象

Statement pt= conn.createStatement();
String sql = “sql语句”

4.执行SQL语句

增删改:(int)pt.excuteUpdate(sql )
查: (ResultSet)pt.excuteQuery()

5.关闭连接

ResultSet.close();
pt.close();
conn.close();

Spring-jdbc

Spring框架封装了一个数据源的模型类:DriverManagerDataSource。通过这个可以构造数据源模型,用于访问数据源。一般需要设置以下几个属性:
1.driverClassName:驱动类型
2.url:数据源连接地址
3.username:连接用户名
4.password:连接密码

DriverManagerDataSource 的底层就是 DriverManager.getConnection() 的操作。

xml中配置jdbcTemplate




    
        
    

    
        
        
        
        
    

然后就可以使用jdbcTemplate进行sql操作。
增加:

int row = jdbcTemplate.update("insert into user_info(name, tel) values (?, ?)", "heihei", "200");

更改:

int row = jdbcTemplate.update("update user_info set tel = ? where name = ?", "54321", "heihei");

删除:

int row = jdbcTemplate.update("delete from  user_info where name = ?", "heihei");

查询:
1.将结果封装为User类型。

List userList = jdbcTemplate.query("select * from  user_info", new BeanPropertyRowMapper<>(User.class));

2.也可以直接指定返回的类型:queryForObject 。这个 SQL 返回的必定是一个只有一行一列的数据

   Long count = jdbcTemplate.queryForObject("select count(id) from user_info ", Long.class);

3.根据传入的类型,返回结果集合。只能是查询结果只有一列时,才可以指定的:

List userList = jdbcTemplate.queryForList("select name from tbl_user", String.class);

4.不指定唇乳的类型,返回结果用Map封装

List> userList = jdbcTemplate.queryForList("select name from tbl_user");
JdbcTemplate的扩展NamedParameterJdbcTemplate

使用jdbcTemplate:

jdbcTemplate.queryForList("select * from user_info where id > ? and name like ?", 3, "ha%");

如上,这条 SQL 有两个参数,后面传入的参数必须要保证顺序与 SQL 一致。

使用NamedParameterJdbcTemplate:

select * from  user_info where id > :id and name like :name

总结:
JdbcTemplate 是靠 ? 占位符 + 可变参数。
NamedParameterJdbcTemplate 的套路是靠参数变量名和 Map 设置了。

Map params = new HashMap<>();
    params.put("id", 1);
    params.put("name", "xiao%");
    List> userList = jdbcTemplate
        .queryForList("select * from user_info where id > :id and name like :name", params);
大文本的处理

大字段的核心 API 是一个 LobHandler 和 LobCreator。
在配置类中新一个@Bean。LobHandler

   @Bean
    public LobHandler lobHandler() {
        return new DefaultLobHandler();
    }

在业务中,可以通过lobHandler的相关方法获取大字段。

JDBC事务 事务有四大特性:

1.原则性:一个事务就是一个不可分割的单位。一个事务内的操作,要么全部做,要么全部不做。

2.一致性:事务执行后,所有的数据都应该保持一致性。

3.隔离性:多个数据库操作并发执行时,一个请求的事务操作不能被其他操作干扰,多个并发事务执行之间要相互隔离。隔离性强调的是并发的隔离。

4.持久性:事务执行后,它对数据的影响是永久性的。

并发事务操作出现的问题:

1.脏读:一个事务读取到了另一个事务没有提交的数据。

2.不可重复读:一个事务读取到了另一个事务已经修改提交的数据。对同一行数据查询两次,结果不一致。

3.幻读:一个事务读取到另一个事务已经新增的数据。对同一张表查询两次,出现新增的行,导致结果不一致。

隔离级别

针对并发事务出现的问题。引入了事务的隔离级别:
1.read uncommitted:读未提交—不解决任何问题

2.read committed:读已提交—解决脏读

3.repeatable read:可重复度—解决脏读,不可重复读

4.serializable:可串行化—解决脏读,不可重复读,幻读

MySQL中默认的事务隔离级别是:repeatable read。
Oracle默认的事务隔离级别是read committed。

事务的保存点

一些场景,可能需要分段执行,如果出现异常不需要全部回滚。这时候就需要用到保存点。
1.通过数据源获取到数据库连接对象Connection

2.通过连接对象设置保存点
savepoint = Connection.setSavepoint();

3.异常的时候回退到保存点
Connection.rollback(savepoint)
Connection.commit();//提交保存点之前的事务

Spring框架中引入编程式事务

1.配置事务相关的组件


    



    

DataSourceTransactionManager:事务管理器,负责控制事务。
TransactionTemplate:事务模板,使用它可以完成编程式事务。

2.在业务中可以直接注入transactionTemplate
在业务中直接使用 transactionTemplate.execute方法,这个方法需要传入一个TransactionCallback 类型的对象。TransactionCallback 是一个函数式接口:

@FunctionalInterface
public interface TransactionCallback {
	T doInTransaction(TransactionStatus status);
}

代码中就可以这样使用:

 transactionTemplate.execute(status -> {
        userDao.save(user);

        int i = 1 / 0;

        List userList = userDao.findAll();
        System.out.println(userList);
        //返回execute方法的执行结果
        return null;
    });

Spring框架中提供了一个:TransactionCallbackWithoutResult 。就是用来封装返回null的操作。然后就可以直接用这个类。

 transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            userDao.save(user);

            int i = 1 / 0;

            List userList = userDao.findAll();
            System.out.println(userList);
        }
    });
DataSourceTransactionManager

它是基于数据源的事务管理器。它实现的根接口PlatformTransactionManager 定义了commit和rollback方法:

public interface PlatformTransactionManager extends TransactionManager {
	TransactionStatus getTransaction(TransactionDefinition definition)
			throws TransactionException;
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}

这个 commit 和 rollback 方法要传入一个 TransactionStatus 的参数。

TransactionTemplate

这个和JdbcTemplate是类似的。都是提供一个模板来完成平时比较复杂的工具。
它的核心方法是来自 TransactionOperations 接口定义的 execute 方法:

@Override
@Nullable
public  T execute(TransactionCallback action) throws TransactionException {
    Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
    }
    else {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result;
        try {
            result = action.doInTransaction(status);
        }
        catch (RuntimeException | Error ex) {
            // 业务代码出现异常,回滚事务
            rollbackOnException(status, ex);
            throw ex;
        }
        catch (Throwable ex) {
            // 业务代码出现异常,回滚事务
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
        }
        // try块没有出现异常,业务代码执行成功,提交事务
        this.transactionManager.commit(status);
        return result;
    }
}

它帮我们完成事务的相关操作。

Spring框架中声明式事务 xml配置声明式事务

对于 xml 配置文件的声明式事务,需要引入新的命名空间了:tx ,它就是关于事务的配置部分。

引入依赖:Aop整合AspectJ


    org.aspectj
    aspectjweaver
    1.9.5


    
        
        
    

AOP配置中引入这个通知:


    

tx:method的其它属性
1.isolation :事务隔离级别。默认是 DEFAULT ,即依据数据库默认的事务隔离级别来定。

2.timeout :事务超时时间,当事务执行超过指定时间后,事务会自动中止并回滚,单位 秒 。默认值 -1 ,代表永不超时。

3.read-only :设置是否为只读事务。默认值 false ,代表读写型事务。
当设置为 true 时,当前事务为只读事务,通常用于查询操作(此时不会有 setAutoCommit(false) 等操作,可以加快查询速度)。

4.rollback-for :当方法触发指定异常时,事务回滚,需要传入异常类的全限定名。
默认值为空,代表捕捉所有 RuntimeException 和 Error 的子类。
一般情况下,在日常开发中,我们都会显式声明其为 Exception ,目的是一起捕捉非运行时异常。

5.no-rollback-for :当方法触发指定异常时,事务不回滚继续执行,需要传入异常类的全限定名。默认值为空,代表不忽略异常。

6.propagation :事务传播行为。

基于注解驱动的声明式事务

配置类上开启事务–@EnableTransationManagement

@Configuration
@ComponentScan("com.linkedbear.spring.transaction.d_declarativeanno")
@EnableTransactionManagement
public class DeclarativeTransactionConfiguration {

业务代码中通过@Transational开启注解
@Transational的其他属性和xml中配置的几乎一样

@Transactional
public void save() {
    userDao.save(user);
}

也可以通过xml配置,开启注解声明事务


Spring框架中事务传播行为

事务与事务之间,如何决定事务的行为?

事务的传播行为:外层的事务传播到内层事务中,内层事务作出的行为。这就是事务传播行为。

Spring框架定义了7中事务传播行为:
1.REQUIRED:必须的【默认值】
含义:如果当前没有事务运行,则会开启一个新的事务。如果当前已经有事务,则方法会运行在当前事务中。

2.REQUIRES_NEW:新事物
含义:如果当前没有事务运行,则会开启一个新的事务。如果当前已经有事务运行,则会将当前事务挂起,重新开启一个新的事务。当新的事务运行完毕,再将原来的事务释放。

3.SUPPORTS:支持
含义:如果当前有事务运行,则方法会运行在当前事务中。如果当前没有事务运行,则不会创建新的事务。

4.NOT_SUPPORTED:不支持
如果当前有事务运行,则会将该事务挂起。如果当前没有事务运行,则它也不会运行在事务中。

5.MANDATORY:强制
当前方法必须运行在事务中,如果没有事务,则直接抛出异常。

6.NEVER:不允许
当前方法不允许运行在事务中,如果当前已经有事务运行,则抛出异常。

7.NESTED:嵌套
如果当前没有事务运行,则开启一个新的事务;如果当前已经有事务运行,则会记录一个保存点,并继续运行在当前事务中。如果子事务运行中出现异常,则不会全部回滚,而是回滚到上一个保存点。

在xml中配置传播行为


    
        
        
    

注解配置传播行为

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testAnnotationTransation() {
    System.out.println("testAnnotationTransation......");
}

运行事务确定
要想知道运行的是哪个事务。通过TransactionSynchronizationManager 。
在方法内,通过TransactionSynchronizationManager.getCurrentTransactionName()。就可以获取当前事务的名称(事务的名称就是开启事务时触发的方法)。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/353499.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号