java程序员入行科目一之CRUD轻松入门教程(四)

三层架构

所谓的三层:

  • DAO层
    • 也可以叫做Mapper或者是Repository层,名字无所谓,但是要清楚这层就是专门和数据库做交互的,一般指的是关系型数据库
  • Service层
    • 数据的加工处理,调动DAO层去完成一些具体的业务实现,包括事务的控制。
  • Web层
    • 现在还没有学习关于Web的知识,现在基本都是根据main方法或者是控制台输入来实现一些交互,到了后期,会通过前端的页面或者其他的方式和这层做基本交互。
    • 手机用户的数据和需求,并且给用户返回需要展示的数据。

其次是关于包的命名

image.png

  • dao:放和数据库交互的DAO层接口和实现类
  • entity:放实体类,实体类是和数据库中的表做映射的。
  • service:放做业务处理的service层的接口和实现类。
  • utils:一般存放一些通用性的工具。
  • web:后期会存放Servlet的东西,完成和页面之间的交互(现在还没学!!)

DAOUtils

在DAO层中,现在依然有大量的冗余代码在,比如获取连接,什么PreparedStatement等等的操作,都是重复性的,能不能再次封装一封,让DAO层的操作变的更简单

  • 写操作:
    • SQL语句是什么?
    • 占位符要赋什么值?
  • 读操作:
    • SQL语句是什么?
    • 占位符要赋什么值?
    • 返回结果的封装?

封装写操作

声明一个DaoUtils的工具类,在这个工具类中完成写操作的封装。

package com.jimihua.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 将写操作和读操作再做一层封装!
 */
public class DaoUtils {


    /**
     * 公共处理增删改三个操作的方法
     * @param sql   SQL语句
     * @param args  占位符要赋的值
     * @return
     */
    public static int commonUpdate(String sql,Object... args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            //1、获取连接
            conn = DatabaseUtils.getConnection();
            //2、构建PreparedStatement对象
            ps = conn.prepareStatement(sql);
            //3、给占位符赋值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            //4、执行SQL,获取返回结果
            count = ps.executeUpdate();
        }finally {
            //5、释放资源
            DatabaseUtils.closeAll(null,ps);
        }
        //6、响应返回结果
        return count;
    }

}

去优化PersonDao和AccountDao中的各种写操作进行优化,简化代码!

封装读操作

读操作需要考虑一下执行SQL语句之后,会拿到ResultSet,需要将Result封装对应实体类。

需要提供一个方法,这个方式可以基于泛型指定具体的类型,不同的实体类各自去重写方法实现即可。

因为之前DaoUtils的工具,提供了static方法,但是static方法不能和泛型一起用,为了解决,将之前的写操作的通用方法改造成非静态方法。

上述操作完成后,优先声明一个接口,RowMapper。

package com.jimihua.rowmapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 向上抽取一个RowMapper接口,提供一个将resultSet封装为对应实体类的方法
 * @param <T>
 */
public interface RowMapper<T> {


    /**
     * 这个方法需要不同的Dao,不同的实体类各自去实现即可。
     * @param rs
     * @return
     * @throws SQLException
     */
    T rowMapper(ResultSet rs) throws SQLException;

}


在DaoUtils工具类中,声明对应的公共查询方法

package com.jimihua.utils;

import com.jimihua.rowmapper.RowMapper;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 将写操作和读操作再做一层封装!
 */
public class DaoUtils<T> {


    /**
     * 公共处理增删改三个操作的方法
     * @param sql   SQL语句
     * @param args  占位符要赋的值
     * @return
     */
    public int commonUpdate(String sql,Object... args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            //1、获取连接
            conn = DatabaseUtils.getConnection();
            //2、构建PreparedStatement对象
            ps = conn.prepareStatement(sql);
            //3、给占位符赋值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            //4、执行SQL,获取返回结果
            count = ps.executeUpdate();
        }finally {
            //5、释放资源
            DatabaseUtils.closeAll(null,ps);
        }
        //6、响应返回结果
        return count;
    }



    public List<T> commonSelect(String sql, RowMapper<T> rowMapper,Object... args) throws SQLException {
        //1、获取连接
        Connection conn = DatabaseUtils.getConnection();

        //2、基于SQL构建PreparedStatement
        PreparedStatement ps = conn.prepareStatement(sql);

        //3、占位符赋值
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i + 1,args[i]);
        }

        //4、执行SQL,拿到ResultSet结果集
        ResultSet resultSet = ps.executeQuery();

        //5、封装结果集
        List<T> list = new ArrayList<T>();
        while(resultSet.next()){
            T t = rowMapper.rowMapper(resultSet);
            list.add(t);
        }

        //6、释放资源
        DatabaseUtils.closeAll(conn,ps,resultSet);

        //7、响应返回结果
        return list;
    }
}


为了可以在具体的Dao中使用DaoUtils的公共查询方法,需要将RowMapper做对应的实现

PersonRowMapper:

package com.jimihua.rowmapper.impl;

import com.jimihua.entity.Person;
import com.jimihua.rowmapper.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 将ResultSet结果集封装为Person对象的方法
 */
public class PersonRowMapper implements RowMapper<Person> {


    @Override
    public Person rowMapper(ResultSet resultSet) throws SQLException {
        Person person = new Person();
        person.setId(resultSet.getInt("id"));
        person.setName(resultSet.getString("name"));
        person.setAge(resultSet.getInt("age"));
        person.setBornDate(resultSet.getDate("born_date"));
        person.setEmail(resultSet.getString("email"));
        person.setAddress(resultSet.getString("address"));
        return person;
    }
}

AccountRowMapper:

package com.jimihua.rowmapper.impl;

import com.jimihua.entity.Account;
import com.jimihua.rowmapper.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 *  将ResultSet封装为Account对象的RowMapper
 */
public class AccountRowMapper implements RowMapper<Account> {

    @Override
    public Account rowMapper(ResultSet rs) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setCardNo(rs.getString("card_no"));
        account.setPwd(rs.getString("pwd"));
        account.setMoney(rs.getBigDecimal("money"));
        return account;
    }
}


改造对应的DaoImpl实现的查询方法

PersonDaoImpl:

package com.jimihua.dao.impl;

import com.jimihua.dao.PersonDao;
import com.jimihua.entity.Person;
import com.jimihua.rowmapper.impl.PersonRowMapper;
import com.jimihua.utils.DaoUtils;

import java.sql.SQLException;
import java.util.List;

/**
 * 实现PersonDao接口,实现内5个方法。
 */
public class PersonDaoImpl implements PersonDao {


    private DaoUtils<Person> daoUtils = new DaoUtils();

    // 省略写操作代码

    @Override
    public Person findById(Integer id) throws Exception {
        //1、编写SQL语句
        String sql = "select * from person where id = ?";

        //2、调用DaoUtils
        List<Person> list = daoUtils.commonSelect(sql, new PersonRowMapper(), id);

        //3、返回结果
        return list != null && list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<Person> findAll() throws SQLException {

        //1、编写SQL语句
        String sql = "select * from person";

        //2、调用DaoUtils
        List<Person> list = daoUtils.commonSelect(sql, new PersonRowMapper());

        //3、返回结果
        return list;
    }
}

AccountDaoImpl:

package com.jimihua.dao.impl;

import com.jimihua.dao.AccountDao;
import com.jimihua.entity.Account;
import com.jimihua.rowmapper.impl.AccountRowMapper;
import com.jimihua.utils.DaoUtils;

import java.sql.SQLException;
import java.util.List;

public class AccountDaoImpl  implements AccountDao {

    private DaoUtils<Account> daoUtils = new DaoUtils();

    @Override
    public Account findByCardNo(String cardNo) throws SQLException {
        //1、编写SQL语句
        String sql = "select * from account where card_no = ?";

        //2、执行daoUtils查询
        List<Account> list = daoUtils.commonSelect(sql, new AccountRowMapper(), cardNo);

        //8、返回结果
        return list != null && list.size() > 0 ? list.get(0) : null;
    }
    // 省略修改操作

}

Druid连接池

在和数据库交互的时候,需要基于DriverManager去构建一个Connection对象,与数据库交互完毕后,还需要将这个Connection对象释放掉。这样 频繁的创建和释放Connection对象有点消耗资源。

连接池的池化技术就是来解决这个问题的。

Ps:其次连接池也可以更好的对Connection的操作做监控,以及根据项目的情况更好的去指定Connection的个数。

准备Druid连接池jar文件

依然是去mvnrepository.com中去下载对应的Druid连接池的jar文件

https://mvnrepository.com/artifact/com.alibaba/druid/1.2.15

下载完毕后,记得将jar包添加到工程中。

image.png

改造DatabaseUtils

之前获取连接对象,依然是通过DriverManager去get的,现在需要先初始化好 DruidDataSource 对象,并且设置好他需要的核心信息,后期再获取Connection时,要基于 DruidDataSource 去get到。

其次,在执行连接池提供的Connection的close方法时,会从原来的释放资源,变为归还给连接池,释放资源的代码不需要动!

初始化 DruidDataSource时,需要将连接数据库的几个信息交给 DruidDataSource ,最核心的是四个。

  • 驱动类的全路径
  • 连接数据库的url信息
  • 用户名
  • 密码

DatabaseUtils改造后的内容:

package com.jimihua.utils;


import com.alibaba.druid.pool.DruidDataSource;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 数据库操作的一个通用的内容
 */
public class DatabaseUtils {

    private static DruidDataSource dataSource = new DruidDataSource();

    private static final Properties PROP = new Properties();

    private static final ThreadLocal<Connection> tl = new ThreadLocal<>();

    // 加载database.properties文件。 同时将信息设置给dataSource   
    static{
        try {
            // 优先通过PROP对象,加载database.properties文件
            InputStream is = DatabaseUtils.class.getResourceAsStream("/database.properties");
            PROP.load(is);
            // 给dataSource设置四个核心信息
            dataSource.setDriverClassName(PROP.getProperty("jdbc.driver"));
            dataSource.setUrl(PROP.getProperty("jdbc.url"));
            dataSource.setUsername(PROP.getProperty("jdbc.username"));
            dataSource.setPassword(PROP.getProperty("jdbc.password"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

 

    /**
     * 获取Connection对象方法
     * @return
     */
    public static Connection getConnection() {
        //1、先尝试从ThreadLocal中获取。
        Connection conn = tl.get();
        if (conn == null) {
            //2、没获取到,基于DriverManager获取Connection,然后添加到ThreadLocal中
            try {
                 // 基于连接池获取Connection对象
                conn = dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException("获取Connection出错!");

            }
            tl.set(conn);
        }
        //3、获取到了,直接返回
        return conn;
    }
    // 省略部分功能代码
}

Apache的DBUtils

Commons DBUtils的工具,是Apache组织提供的一个对JDBC进行简单封装的开源工具类库。他可以简化咱们和数据库交互的操作,让更多的精力都放在编写SQL语句上。

DBUtils基本使用(增删改)

1、下载jar包。

2、将jar导入到项目当中。

3、需要给DatabaseUtils提供一个返回dataSource的静态方法。

4、可以在需要操作的DaoImpl类中,声明一个QueryRunner的类,传入dataSource。

5、完成基本的写操作。


下载jar包:直接去mvnrepository.com去下载。

https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils/1.8.1

将下载好的jar文件,添加到项目中………………

导入完毕后,测试一下,在任意类中编写QueryRunner得有提示…………


给DatabaseUtils提供返回DataSource的静态方法

-public class DatabaseUtils {

 private static DruidDataSource dataSource = new DruidDataSource();

 public static DataSource getDataSource(){
     return dataSource;
 }
 // 省略其他代码
}

根据数据库中仅存的user表,提供对应的UserDao以及UserDaoImpl。

在UserDaoImpl中去初始化QueryRunner,并传入DataSource

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;

public class UserDaoImpl  implements UserDao {

 // 初始化QueryRunner
 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 @Override
 public int insert(User user) {
     return 0;
 }
}


在这完成增删改操作。

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.SQLException;

public class UserDaoImpl  implements UserDao {

 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 @Override
 public int insert(User user) throws SQLException {
     // 准备SQL
     String sql = "insert into user(username,password) values(?,?)";
     // 基于QueryRunner执行SQL语句,传入占位符参数,获取结果
     int count = queryRunner.update(sql, user.getUsername(), user.getPassword());
     // 返回几行受影响
     return count;
 }

 @Override
 public int updateById(User user) throws SQLException {
     String sql = "update user set username=?,password=? where id=?";
     int count = queryRunner.update(sql, user.getUsername(), user.getPassword(), user.getId());
     return count;
 }

 @Override
 public int deleteById(Long id) throws SQLException {
     String sql = "delete from user where id=?";
     int count = queryRunner.update(sql, id);
     return count;
 }
}

查询操作

查询操作相比写操作,只需要多关注一下返回的ResultSet结果集如何封装为对应的entity对象。

这个操作在DBUtils中,他已经提供对应的方式。

首先DBUtils的查询操作要调用QueryRunner的query方法执行SQL。

至于返回结果的封装,可以在query方法中基于传入不同Bean,可以让DBUtils做不同封装。

  • BeanHandler:将一行返回结果,封装为一个对象。当前query方法返回一个entity实例。
  • BeanListHandler:将多行返回结果,封装为一个集合。当前query方法返回一个List<entity>实例。
  • ScalarHandler:只返回第一行第一列的一个结果。

直接完成针对UserDaoImpl中的查询操作

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl  implements UserDao {

 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 // 省略写操作代码

 @Override
 public User selectById(Long id) throws SQLException {
     String sql = "select * from user where id = ?";
     User user = queryRunner.query(sql, new BeanHandler<User>(User.class), id);
     return user;
 }

 @Override
 public List<User> selectAll() throws SQLException {
     String sql = "select * from user";
     List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));
     return userList;
 }

 @Override
 public Long selectCount() throws SQLException {
     String sql = "select count(*) from user";
     Long count = queryRunner.query(sql, new ScalarHandler<Long>());
     return count;
 }
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鷄米花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值