增删改查的工具类
操作数据库常用的执行方法:
execute()可以进行增删改查
executeUpdate() 可以执行增删改 但是不能执行查询
exeuctQuery():只可以执行查询
我们在封装这个工具类的时候,只需要封装两种:一种用来执行增删改操作,一种用来执行查询操作。
一般我们会将数据库中的表封装成一个类,那么表中的一条记录就对应这一个对象,字段就是类的属性。
因此一般我们也将与数据库直接交互的这一层代码称之为DAO层(Database access Object)
反射和泛型:
泛型:参数化类型 用在类上 方法上 接口上
反射:通过Class对象获得类中的属性,方法 ,构造方法
可以通过反射来创建对象,调用方法 ,访问属性或者为属性赋值
// 增删改查的工具类
public class CRUDTemplate {
/**
* 增删改操作
* @param sql sql语句
* @param params sql语句中的参数
* @return
*/
public static int executeUpdate(String sql,Object ... params){
Connection conn = null;
PreparedStatement pstmt = null;
int rows = 0;
try {
// 获取连接
conn = JDBCUtil.getConnection();
//获取sql语句的预编译对象
pstmt = conn.prepareStatement(sql);
//给占位符赋值
for (int i= 0 ;i <params.length;i++){
pstmt.setObject(i +1,params[i]);
}
// 执行sql
rows = pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtil.releaseSource(null,pstmt,conn);
}
return rows;
}
/**
* 查询操作
* @param sql
* @param handler
* @param params
* @return
* @param <T>
*/
public static <T> T executQuery(String sql,IResultSetHandler<T> handler, Object
... params){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
conn = JDBCUtil.getConnection();
pstmt = conn.prepareStatement(sql);
for(int i = 0 ; i < params.length;i++){
pstmt.setObject(0 + 1,params[i]);
}
//执行sql 返回结果集
rs = pstmt.executeQuery();
//调用专门的结果集处理接口来处理结果集 至于返回的是什么类型的结果 有结果集处理器决定
return handler.handle(rs);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtil.releaseSource(rs,pstmt,conn);
}
}
}
这是一个数据库操作工具类,提供了增删改查的方法,其中:
executeUpdate()方法用于执行增删改操作,接收一个SQL语句和参数列表,返回受
影响的行数。
executQuery()方法用于执行查询操作,接收一个SQL语句、一个结果集处理器和参
数列表,返回处理后的结果。
该工具类中使用了JDBCUtil类提供的getConnection()和releaseSource()方法获取
和释放数据库连接。
该工具类中还使用了一个IResultSetHandler接口,用于处理查询结果集并返回指定
类型的结果。
//返回单个对象
public class BeanHandler<T> implements IResultSetHandler<T>{
private Class<T> clazz;
public BeanHandler(Class<T> clazz){
this.clazz = clazz;
}
@Override
public List<T> handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException {
List<T> list = new ArrayList<>();
if (rs.next()){
T obj = clazz.newInstance();//创建一个实例对象
// 获取字节码信息 拿到该类的属性 方法 等信息封装成BeanInfo
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
// 获取所有的属性信息 获取所有的属性名称
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds){// 对每一个属性 依次读取数据库中的字段对应的值
并进行赋值操作
// 获取字段名称 将来在创建实体类的时候 要求属性名必须和字段名一致
Object o = rs.getObject(pd.getName());
//执行当前方法并传入参数
pd.getWriteMethod().invoke(obj,o);
}
list.add(obj);
if (list.size()>0)
return list;
}
return null;
}
}
public class BeanListHandler<T> implements IResultSetHandler<T>{
private Class<T> clazz;
public BeanListHandler(Class<T> clazz){
this.clazz = clazz;
}
@Override
public List<T> handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException {
// 获取字节码信息
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
// 获取指定的属性信息
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
List<T> list = new ArrayList<>();
while(rs.next()){
T obj = clazz.newInstance();
for (PropertyDescriptor pd :pds){
// 获取字段名=属性名
Object o = rs.getObject(pd.getName());
pd.getWriteMethod().invoke(obj,o);
}
list.add(obj);
}
return list;
}
}
//把jdbc返回的结果集封装成特定类型
public interface IResultSetHandler<T> {
//专门用来处理结果集
// T handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException;
List<T> handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException;;
}
public class CRUDTemplateTest {
public static void main(String[] args) {
String sql = "select * from users ";
List<User> users = CRUDTemplate.executQuery(sql,new BeanListHandler<User>
(User. class));
System.out.println(users.size());
}
}
CRUD的工具类
//返回单个对象
public class BeanHandler<T> implements IResultSetHandler<T>{
private Class<T> clazz;
public BeanHandler(Class<T> clazz){
this.clazz = clazz;
}
@Override
public T handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException {
if (rs.next()){
T obj = clazz.newInstance();//创建一个实例对象
// 获取字节码信息 拿到该类的属性 方法 等信息封装成BeanInfo
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
// 获取所有的属性信息 获取所有的属性名称
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds){// 对每一个属性 依次读取数据库中的字段对应的值
并进行赋值操作
// 获取字段名称 将来在创建实体类的时候 要求属性名必须和字段名一致
Object o = rs.getObject(pd.getName());
//执行当前方法并传入参数
pd.getWriteMethod().invoke(obj,o);
}
return obj;
}
return null;
}
}
public class BeanListHandler<T> implements IResultSetHandler<List<T>>{
private Class<T> clazz;
public BeanListHandler(Class<T> clazz){
this.clazz = clazz;
}
@Override
public List<T> handle(ResultSet rs) throws SQLException, InstantiationException,
IllegalAccessException, IntrospectionException, InvocationTargetException {
// 获取字节码信息
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
// 获取指定的属性信息
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
List<T> list = new ArrayList<>();
while(rs.next()){
T obj = clazz.newInstance();
for (PropertyDescriptor pd :pds){
// 获取字段名=属性名
Object o = rs.getObject(pd.getName());
pd.getWriteMethod().invoke(obj,o);
}
list.add(obj);
}
return list;
}
}
DBUtils(应用)
DButils是一个为做一些准备如关闭连接,装载驱动 获得连接 提交事务 回滚事务 所有的方法都是静
态的
QueryRunner 简化了sql查询,CRUD
ResultSetHandler 接口 结果集的处理接口
public class DBUtilsTest {
@Test
public void insertData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "insert into tb_user(name,age,status,gender)values(?,?,?,?)";
int rows = qr.update(conn,sql,"张三",22,1,"男");
System.out.println(rows);
DbUtils.closeQuietly(conn);
}
@Test
public void updateData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "update tb_user set name =? where id=?";
int rows = qr.update(conn,sql,"李四",4);
System.out.println(rows);
DbUtils.closeQuietly(conn);
}
@Test
public void deleteData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "delete from tb_user where id=?";
int rows = qr.update(conn,sql,4);
System.out.println(rows);
DbUtils.closeQuietly(conn);
}
@Test
public void selectOneData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "select * from tb_user where id=?";
User user = qr.query(conn,sql, new BeanHandler<>(User.class),1);
System.out.println(user);
DbUtils.closeQuietly(conn);
}
@Test
public void selectAllData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "select * from tb_user ";
List<User> users = qr.query(conn,sql, new BeanListHandler<>(User.class));
System.out.println(users);
DbUtils.closeQuietly(conn);
}
}
DBUtils对于事务的支持
/**
* 数据访问层
*/
public class BankDao {
/**
* 加钱的方法
* @param account 被转账的账户
* @param money 转账的金额
* @return
*/
public int addMoney(Connection conn,String account, int money) throws
SQLException {
String sql = "update account set money = money+? where aname=?";
QueryRunner qr = new QueryRunner();
int rows = qr.update(conn,sql,account,money);
return rows;
}
/**
* 减钱的方法
* @param account 转账的账户
* @param money 转账的金额
* @return
*/
public int subMoney(Connection conn,String account,int money) throws
SQLException {
String sql = "update account set money = money-? where aname=?";
QueryRunner qr = new QueryRunner();
int rows = qr.update(conn,sql,money,account);
return rows;
}
}
/**
* 业务类 实现转账操作
*/
public class BankService {
private BankDao dao = new BankDao();
public void transfer(String addAccount,String subAccount,double money){
Connection conn = JDBCUtil.getConnection();
// 开启事务(关闭自动提交)
try {
conn.setAutoCommit(false);
int i = dao.addMoney(conn,addAccount,money);
System.out.println(i);
System.out.println(10/0);
int a = dao.subMoney(conn,subAccount,money);
System.out.println(a);
DbUtils.commitAndClose(conn);
} catch (SQLException e) {
DbUtils.rollbackAndCloseQuietly(conn);
}
}
}
当字段名和属性名不一致的时候
在查询的时候,给字段起别名 这样就可以结局不一致的问题。但是别名应该和属性名一致
@Test
public void selectOneData() throws SQLException {
Connection conn = JDBCUtil.getConnection();
//创建sql执行工具
QueryRunner qr = new QueryRunner();
String sql = "select id, name username,age userAge,status,gender from
tb_user where id=?";
User user = qr.query(conn,sql, new BeanHandler<>(User.class),1);
System.out.println(user);
DbUtils.closeQuietly(conn);
}
当使用dbutils进行查询的时候,要么实体的属性名和字段名一致,要么给字段起别名 ,别名和属性名一致。
如果数据库中的字段名由多个单词组成,中间以下划线分隔。此时属性名应该遵循小驼峰命名法进行命名,
在查询数据看的时候,使用别名解决
数据库连接池技术
网址
常用的数据库连接池:DBCP、C3p0 国货之光 druid
JDBC的数据库连接池使用javax.sql.DataSource接口进行规范。
Druid数据库连接池的使用
通过硬编码的方式 得到连接池对象 从中获得连接
public class DruidTest {
@Test
public void druidTest() throws SQLException {
DruidDataSource dds = new DruidDataSource();
dds.setDriverClassName("com.mysql.cj.jdbc.Driver");
dds.setUrl("jdbc:mysql://localhost:3306/studydb");
dds.setUsername("root");
dds.setPassword("root");
// 获取连接
Connection conn = dds.getConnection();
System.out.println(conn);
conn.close();//释放连接 释放连接池
}
}
通过外部配置:
//通过配置文件的方式得到Datasources
@Test
public void druidTest1() throws Exception {
Properties prop = new Properties();
InputStream in =
DruidTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
prop.load(in);
DataSource ds = DruidDataSourceFactory.createDataSource(prop);
System.out.println(ds.getConnection());
}
其他配置
driverClassName = com.mysql.cj.jdbc.Driver
url= jdbc:mysql://localhost:3306/studydb
username=root
password=root
#初始化连接数
initialSize=1
# 最小的空闲的连接数
minIdle=2
#最大的连接数
maxActive =5
密码的处理
常见的加密方式:Base64加密算法,MD5加密,对称加密算 ,非对称加密算法,数字签名算法,数字整
数,CA认真等。
加密方式的使用场景:
- Base64:图片的转码(发送邮件,img标签,http加密)
- MD5加密:密码加密,文件的校验
- 非对称加密:应用在互联网电商 订单付款,银行业务
MD5加密
基本的加密算法的底层原理
public class MD5Test {
public static void main(String[] args) {
String str = "123456";
// 生成普通的MD5的密码
MessageDigest md5 = null;
try {
//创建加密对象 同时指定加密算法为md5
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
char[] charArray = str.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++) {
byteArray[i] = (byte) charArray[i];
}
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int j = 0; j < md5Bytes.length; j++) {
int val = (int) md5Bytes[j] & 0xff;
if (val < 16) {
hexValue.append("0");
} else {
hexValue.append(Integer.toHexString(val));
}
}
System.out.println(hexValue.toString());
}
}
使用工具类
Apache Commons Codec 1.15
public static void main(String[] args) {
String encode = DigestUtils.md5Hex("abc123456");
System.out.println(encode);
}
}
加严加密:
public static void main(String[] args) {
//普通加密
String encode = DigestUtils.md5Hex("abc123456");
System.out.println(encode);
//加盐加密
String encode2 = Md5Crypt.apr1Crypt("abc123456","sxjzit");
System.out.println(encode2);
}
密码的校验:
MD5加密是不可逆的,在校验的时候 应该是对用户输入的密码在此加密,并且应该使用相同的盐。这样才能
保证两次加密的密文字符串相同。