Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)

Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上):https://developer.aliyun.com/article/1580591

8 Service 业务

8.1 什么是业务

代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成。(软件所提供的一个功能都叫业务)

8.2 转账业务开发

转账业务分析

image.png

public class AccountServiceImpl implements AccountService {
    AccountDao accountDao = new AccountDaoImpl();
    @Override
    public String zhuanZhang(String fromName, String password, String toName, double money) {
        try {
            //1、验证我方用户密码
            Account account = accountDao.selectAccount(fromName);
            if(account == null){
                return "用户名不存在";
            }
            if(!account.getPassword().equals(password)){
                return "用户密码不正确";
            }
            //2、验证余额
            if(account.getMoney() < money){
                return "用户余额不足";
            }
            //3、验证对方用户
            if(accountDao.selectAccount(toName) == null){
                return "对方用户名不存在";
            }
            //4、我方扣钱
            accountDao.updateAccount(fromName,-money);

            System.out.println(10/0);

            //5、对方加钱
            accountDao.updateAccount(toName,money);

            return "转账成功";
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return "转账失败";
    }
}

9 事务

在JDBC 中,获得 Connection 对象开始事务–提交或回滚–关闭连接。其事务操作是

  • conn.setAutoCommit(false);//设置事务为手动提交
  • conn.commit();//手动提交事务
  • conn.rollback();//手动回滚事务

9.1 转账业务实现

public class AccountServiceImpl2 implements AccountService {
    AccountDao accountDao = new AccountDaoImpl();
    @Override
    public String zhuanZhang(String fromName, String password, String toName, double money) {
        Connection conn = JDBCUtils.getConnection();
        try {           
            //1、验证我方用户密码
            Account account = accountDao.selectAccount(fromName);
            if(account == null){
                return "用户名不存在";
            }
            if(!account.getPassword().equals(password)){
                return "用户密码不正确";
            }
            //2、验证余额
            if(account.getMoney() < money){
                return "用户余额不足";
            }
            //3、验证对方用户
            if(accountDao.selectAccount(toName) == null){
                return "对方用户名不存在";
            }
            //4、我方扣钱
            accountDao.updateAccount(fromName,-money);
            //System.out.println(10/0);
            //5、对方加钱
            accountDao.updateAccount(toName,money);
            //提交事务
             conn.commit();
            return "转账成功";
        } catch (Exception throwables) {
            throwables.printStackTrace();
           try {
                //回滚事务
                conn.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return "转账失败";
    }
}

注意:此时Service中的Connection与Dao中的Connection对象不一致,无法实现事务回滚

9.2 解决方案1:传递 Connection

  • 如果使用传递Connection,容易造成接口污染(BadSmell)。
  • 定义接口是为了更容易更换实现,而将 Connection定义在接口中,会造成污染当前接口。

9.3 解决方案2:ThreadLocal

  • 可以将整个线程中(单线程)中,存储一个共享值。
  • 线程拥有一个类似 Map 的属性,键值对结构。

9.4 使用ThreadLocal更新JDBC工具类

/**
 * ThreadLocal<T>:能保存对象,能保证在同一个线程下获取到的对象是同一个
 *     set(T);
 *     get();
 *     remove();
 */
static ThreadLocal<Connection> tl = new ThreadLocal<>();

public static Connection getConnection(){
    //1、从ThreadLocal获取Connection
    //2、获取获取到了connection对象直接返回
    //3、创建Connection对象并存到ThreadLocal中,在进行返回
    Connection conn = tl.get();
    try {
        if(conn == null){
            conn = DriverManager.getConnection(url,username,password);
            tl.set(conn);
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    return conn;
}

此时需要注意,如果关闭了Connetion连接,但是在ThreadLocal中还是保存着Connetion对象。下次会获取到一个已经关闭的Connection对象,所以需要从ThreadLocal中移除

9.5 事务封装

将事务的开启、提交、回滚都封装在工具类中,业务层调用即可。

 //封装事务操作的三个方法
public static void begin(){
    //1、获取Connection对象
    Connection conn = getConnection();
    try {
        //2、开启事务
        conn.setAutoCommit(false);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}
public static void commit(){
    //1、获取Connection对象
    Connection conn = getConnection();
    try {
        //2、提交事务
        conn.commit();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        try {
            //3、关闭Connection资源
            conn.close();
            //从ThreadLocal中将connection移除掉
            tl.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}
public static void rollback(){
    //1、获取Connection对象
    Connection conn = getConnection();
    try {
        //2、回滚事务
        conn.rollback();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        try {
            //3、关闭Connection资源
            conn.close();
            //从ThreadLocal中将connection移除掉
            tl.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

9.6 最终的转账业务

public class AccountServiceImpl2 implements AccountService {
    AccountDao accountDao = new AccountDaoImpl();
    @Override
    public String zhuanZhang(String fromName, String password, String toName, double money) {
        try {
            //开启事务
            JDBCUtils.begin();
            //1、验证我方用户密码
            Account account = accountDao.selectAccount(fromName);
            if(account == null){
                return "用户名不存在";
            }
            if(!account.getPassword().equals(password)){
                return "用户密码不正确";
            }
            //2、验证余额
            if(account.getMoney() < money){
                return "用户余额不足";
            }
            //3、验证对方用户
            if(accountDao.selectAccount(toName) == null){
                return "对方用户名不存在";
            }
            //4、我方扣钱
            accountDao.updateAccount(fromName,-money);
            //System.out.println(10/0);
            //5、对方加钱
            accountDao.updateAccount(toName,money);
            //提交事务
            JDBCUtils.commit();
            return "转账成功";
        } catch (Exception throwables) {
            throwables.printStackTrace();
            //回滚事务
            JDBCUtils.rollback();
        }
        return "转账失败";
    }
}

10 三层架构

三层架构原理

image.png

三层架构下包结构

image.png

11 单元测试

11.1 单元测试

/**
 * 单元测试:对已经编写完成的类、模块、方法进行测试
 
 * 使用步骤:
 *  1、导入单元测试的两个jar包(与驱动包导入一致)
 *  2、编写方法进行测试
 
 * 常用的注解:
 *    @Test         单元测试的方法
 *    @Before       在单元测试方法之前执行
 *    @After        在单元测试方法之后执行
 *    @BeforeClass  在类加载之前执行
 *    @AfterClass   在类卸载之后执行
 *
 * 单元测试需要注意的问题
 *    1、@BeforeClass测试的方法必须要加static修饰
 *    2、单元测试的方法不能有参数,不能有返回值
 *    3、不能再单元测试中写Scanner输入内容  
 */
@BeforeClass
public static void testBeforeClass(){
    System.out.println("BeforeClass类加载的时候执行");
}

@Before
public void testBefore(){
    System.out.println("Before在单元测试方法之前执行(自动执行)");
}

@Test
public void test01(){
    System.out.println("单元测试");
}
@After
public void testAfter(){
    System.out.println("After在单元测试方法之后执行(自动执行)");
}
@AfterClass
public static void testAfterClass(){
    System.out.println("BeforeClass类卸载的时候执行");
}

执行结果

BeforeClass类加载的时候执行
Before在单元测试方法之前执行(自动执行)
单元测试
After在单元测试方法之后执行(自动执行)
BeforeClass类卸载的时候执行

11.2 实际应用

在实际开发过程中,我们需要对写好的DAO层代码、Service层代码进行测试

一般就是对DAO、Service层中的每一个方法进行测试

public class AccountTest {
    @Test
    public void testZhuanZhang(){
        AccountService accountService = new AccountServiceImpl2();
        String s = accountService.zhuanZhang("jack","123","rose",200);
        System.out.println(s);
    }
}

12 连接池

12.1 Druid连接池

在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有连接;使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。

  • 创建 db.properties 配置文件。
  • 引入druid的jar 文件,添加到类路径

12.2 db.properties

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///java2303?useSSL=false
username=root
password=123456
#初始化连接   (初始化连接池的,里面默认就已经存在了20个Connection连接)
initialSize=20
#最大连接数量  (当初始的20个连接不够的时候,最大会创建到50个)
maxActive=50
#最小空闲连接  (当连接池中的连接,没有被使用,就会减少到5个)
minIdle=5
#超时等待时间  (当连接数超过最大连接数,会等待5秒,如果5秒后还没有空闲连接,就会抛出异常)
maxWait=5000

12.3 最终版JDBC工具类

DruidDataSourceFactory 导包com.alibaba.druid.pool

public class JDBCUtils {
    //定义数据库连接池
    private static DataSource dataSource;

    //初始化连接池对象
    static{
        try {
            Properties properties = new Properties();
            InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
            properties.load(in);
            //初始化连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //返回连接池对象
    public static DataSource getDataSource(){
        return dataSource;
    }
    //使用ThreadLocal保证Connection在同一个线程下唯一
    static ThreadLocal<Connection> tl = new ThreadLocal<>();
    public static Connection getConnection(){
        Connection conn = tl.get();
        try {
            if(conn == null){
                conn = dataSource.getConnection();
                tl.set(conn);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return conn;
    }
    public static void closeAll(Connection connection, Statement statement , ResultSet rs){
        try {
            if(rs!=null)
                rs.close();
            if(statement!=null)
                statement.close();
            if(connection!=null)
                connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
   }
    public static void begin(){
        Connection conn = getConnection();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public static void commit(){
        Connection conn = getConnection();
        try {
            conn.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            try {
                conn.close();
                tl.remove();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    public static void rollback(){
        Connection conn = getConnection();
        try {
            conn.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            try {
                conn.close();
                tl.remove();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

12.4 连接池测试

public class TestDruidDataSource {
    public static void main(String[] args) throws SQLException {
//        Connection conn1 = JDBCUtils.getConnection();
//        System.out.println(conn1);
//        Connection conn2 = JDBCUtils.getConnection();
//        System.out.println(conn2);
        for (int i = 0; i < 51; i++) {
            Connection connection = JDBCUtils.getDataSource().getConnection();
            System.out.println(connection);
            connection.close();//并不是关闭连接,而是归还到连接池中
        }
    }
}

14 DaoUtils工具类

将Dao层中增删改的代码进行封装

13.1 工具类实现

public class DaoUtils {
    //更新操作(增删改) insert into emp values(?,?,?,?,?,?,?,?)
    public static int commonsUpdate(String sql,Object... args) throws SQLException {
        //1、获取数据库连接对象
        Connection conn = JDBCUtils.getConnection();
        PreparedStatement ps = null;
        try {
            //2、获取数据库操作对象
           ps = conn.prepareStatement(sql);
            //3、设置占位符的值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            //4、执行sql语句
            int count = ps.executeUpdate();
            //5、处理结果
            return count;
        } finally {
            //6、关闭资源
            JDBCUtils.closeAll(null,ps,null);
        }
    }
    public static <T> List<T> commonsQuery(String sql, Class c, Object... args) throws Exception {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1、获取数据库连接对象
            Connection conn = JDBCUtils.getConnection();
            //2、获取数据库操作对象
            ps = conn.prepareStatement(sql);
            //3、设置占位符的值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            //4、执行SQL语句
            rs = ps.executeQuery();
            //5、处理结果集
            List<T> list = new ArrayList<>();
            while(rs.next()){
                //通过类对象获取类的属性
                Field[] fields = c.getDeclaredFields();
                T obj = (T) c.newInstance();
                for (int i = 0; i < fields.length; i++) {
                    //暴力反射
                    fields[i].setAccessible(true);
                    fields[i].set(obj,rs.getObject(i+1));
                }
                //将对象装到List集合中
                list.add(obj);
            }
            return list;
        }finally {
            JDBCUtils.closeAll(null,ps,rs);
        }
    }
}

13.2 DaoUtils工具类使用

public class EmpDaoImpl implements EmpDao {
    @Override
    public int insertEmp(Emp emp) throws SQLException {
        String sql = "insert into emp values(?,?,?,?,?,?,?,?)";
        Object[] args = {emp.getEmpno(),emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno()};
        return DaoUtils.commonsUpdate(sql,args);
    }

    @Override
    public int updateEmp(Emp emp) throws SQLException {
        String sql = "update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?";
        Object[] args = {emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};
        return  DaoUtils.commonsUpdate(sql,args);
    }

    @Override
    public int deleteEmp(int empno) throws SQLException {
        String sql = "delete from emp where empno = ?";
        Object[] args = {empno};
        return DaoUtils.commonsUpdate(sql,args);
    }

    @Override
    public List<Emp> selectAll() throws Exception {
        String sql = "select * from emp";
        return DaoUtils.commonsQuery(sql,Emp.class);
    }
}

14 DBUtils工具类

14.1 DBUtils简介

DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用

  • 对于数据表的查询操作,可以把结果转换为List、Array、Set等集合。便于操作。
  • 对于数据表的DML操作,也变得很简单(只需要写SQL语句)。

14.2 DbUtils核心API

ResultSetHandler接口:转换类型接口

  • BeanHandler类:实现类,把一条记录转换成对象
  • BeanListHandler类:实现类,把多条记录转换成List集合。
  • ScalarHandler类:实现类,适合获取一行一列的数据。
  • MapHandler类: 实现类,把一条记录转换成Map集合
  • MapListHandler类:实现类,把多条记录转换成List集合。
  • QueryRunner:执行sql语句的类
  • 增、删、改:update();
  • 查询:query();

14.3 DbUtils的使用步骤

导入jar包

14.4 DBUtils使用

使用DBUtils实现增删改查

public class EmpDaoImpl implements EmpDao {
    @Override
    public int insertEmp(Emp emp) throws SQLException {
        //1、创建QueryRunner对象
        //如果是更新(增删改)操作,那么就用无参的构造
        QueryRunner qr = new QueryRunner();
        //2、通过QueryRunner对象调用update
        String sql = "insert into emp values(?,?,?,?,?,?,?,?)";
        Object[] args = {emp.getEmpno(),emp.getEname1(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno()};
        return qr.update(JDBCUtils.getConnection(),sql,args);
    }

    @Override
    public int updateEmp(Emp emp) throws SQLException {
        //1、创建QueryRunner对象
        //如果是更新(增删改)操作,那么就用无参的构造
        QueryRunner qr = new QueryRunner();
        //2、通过QueryRunner对象调用update
        String sql = "update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?";
        Object[] args = {emp.getEname1(),emp.getJob(),emp.getMgr(),emp.getHireadate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};
        return qr.update(JDBCUtils.getConnection(),sql,args);
    }

    @Override
    public int deleteEmp(int empno) throws SQLException {
        //1、创建QueryRunner对象
        //如果是更新(增删改)操作,那么就用无参的构造
        QueryRunner qr = new QueryRunner();
        //2、通过QueryRunner对象调用update
        String sql = "delete from emp where empno = ?";
        Object[] args = {empno};
        return qr.update(JDBCUtils.getConnection(),sql,args);
    }
    @Override
    public List<Emp> selectAll() throws Exception {
        //1、创建QueryRunner对象
        //如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        //2、通过QueryRunner对象调用query
        String sql = "select empno,ename ename1,job,mgr,hiredate hireadate,sal,comm,deptno from emp";
        //如果是集合就创建BeanListHandler对象,如果是实体类就创建BeanHandler对象
        List<Emp> empList = qr.query(sql, new BeanListHandler<Emp>(Emp.class));
        return empList;
    }
    @Override
    public Emp selectOne(int empno) throws SQLException {
        //1、创建QueryRunner对象
        //如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        //2、通过QueryRunner对象调用query
        String sql = "select * from emp where empno = ?";
        Object[] args = {empno};
        Emp emp = qr.query(sql, new BeanHandler<Emp>(Emp.class),args);
        return emp;
    }

    @Override
    public long count() throws SQLException {
        //1、创建QueryRunner对象
        //如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        String sql = "select count(*) from emp";
        Long count = qr.query(sql, new ScalarHandler<Long>());
        return count;
    }
}

14.5 字段与属性名不一致

如果数据库的字段名与实体类中的属性名不一致,则无法完成映射,值会显示null

2种解决:

  • 在查询语句中取别名

后期自定义映射

@Override
public List<Emp> selectAll() throws Exception {
    //1、创建QueryRunner对象
    //如果是查询操作,那么就用有参的构造,传递连接池对象(使用完成之后QueryRunner会自动关闭(回收))
    QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
    //2、通过QueryRunner对象调用query
    //通过别名进行数据映射
  String sql = "select empno,ename ename1,job,mgr,hiredate hireadate,sal,comm,deptno from emp";
    //如果是集合就创建BeanListHandler对象,如果是实体类就创建BeanHandler对象
    List<Emp> empList = qr.query(sql, new BeanListHandler<Emp>(Emp.class));
    return empList;
}
public class Emp {
    private Integer empno;
    private String ename1; //这个与数据库字段不一致
    private String job;
    private Integer mgr;
    private Date hireadate; //这个与数据库字段不一致
    private Double sal;
    private Double comm;
    private Integer deptno;
  //省略set、get、构造方法
}

15 Lombok插件

Lombok简化编写类的构造方法、setget方法以及toString方法

使用步骤

开启IEDA注解可用需要在setting中进行设置还需要在setting for newproject中设置
新版本使用lombok插件需要添加配置需要在setting中进行设置还需要在setting for newproject中设置
-Djps.track.ap.dependencies=false

image.png

导入jar包

image.png

编写实体类

image.png



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6天前
|
存储 关系型数据库 MySQL
【Java面试题汇总】MySQL数据库篇(2023版)
聚簇索引和非聚簇索引、索引的底层数据结构、B树和B+树、MySQL为什么不用红黑树而用B+树、数据库引擎有哪些、InnoDB的MVCC、乐观锁和悲观锁、ACID、事务隔离级别、MySQL主从同步、MySQL调优
【Java面试题汇总】MySQL数据库篇(2023版)
|
7天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
7天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
7天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
|
18天前
|
自然语言处理 算法 Java
Java如何判断两句话的相似度类型MySQL的match
【9月更文挑战第1天】Java如何判断两句话的相似度类型MySQL的match
18 2
|
22天前
|
安全 Java 关系型数据库
Java连接Mysql SSL初始化失败
Java连接Mysql SSL初始化失败
|
22天前
|
jenkins Java Shell
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
|
存储 消息中间件 缓存
面试疑难点解析——Java数据库开发(一)
阿里云再offer 5000人,数据库人才求贤若渴!7月9日 19:00,阿里云数据库事业部的两位技术大咖在线直招10多个岗位,想要进入阿里云吗?快来投递简历吧:https://developer.aliyun.com/special/offerday01
面试疑难点解析——Java数据库开发(一)
|
NoSQL Java 关系型数据库
面试疑难点解析——Java数据库开发(七)
本文介绍事务处理的四个特性:ACID,以及开发中可能见到的事务处理模式。
|
算法 Java 数据库连接
面试疑难点解析——Java数据库开发(九)
本文主要教大家区分悲观锁与乐观锁,帮助大家巩固Java开发性相关知识,提高面试通过率。
面试疑难点解析——Java数据库开发(九)