今日内容
0 复习昨日
1 讲作业
2 数据库连接池(druid)
3 反射
4 改造DBUtil
5 完成CRUD练习
0 复习昨日
1 sql注入
2 预处理语句
3 事务操作
4 DBUtil
1 作业【重要】
利用ORM完成,以下的几个方法非常重要,将来写项目就是这些操作
写项目步骤
- 搭建环境
- 创建项目
- 导入依赖
- 工具类
- 数据库对应实体类
- 开发数据库层的代码
- 数据库层代码,一般称为dao (data access object)
- 操作的是User表,所以类UserDao
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
`username` varchar(10) DEFAULT NULL COMMENT '用户名',
`password` varchar(10) DEFAULT NULL COMMENT '密码',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`createTime` date DEFAULT NULL COMMENT '注册时间',
`money` double(10,2) DEFAULT NULL COMMENT '账户余额',
`sex` int(1) DEFAULT NULL COMMENT '性别 1男2女',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
1 根据id从数据库查询出用户信息
public User findUserById(int id)
2 向数据库插入一个用户,返回是否插入成功
public boolean addUser(User user)
3 通过id删除用户数据,返回boolean
public boolean deleteById(int id)
4 设计方法,查询全部数据,返回值是List集合,集合中是全部用户数据
publicList findAll()
5 通过id更改用户数据
public boolean updateById(User user)
注意:根据id更新,即参数User对象中一定有id属性值
更新user表的数据,根据User对象的属性更新,如果属性值是null则不跟更新,不为null就更新对应的字段
返回值是受影响的行数
package com.qf.dao;
import com.qf.model.User;
import com.qf.util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class UserDao {
/**
* 根据id查用户
*
* @param id
* @return
*/
public User findUserById(int id) {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
ps = conn.prepareStatement("select * from tb_user where id = ?");
ps.setInt(1, id);
rs = ps.executeQuery( );
if (rs.next( )) {
user = new User( );
user.setId(rs.getInt("id"));
user.setPhone(rs.getString("phone"));
user.setUsername(rs.getString("username"));
user.setSex(rs.getInt("sex"));
user.setMoney(rs.getDouble("money"));
user.setPassword(rs.getString("password"));
user.setCreateTime(rs.getDate("createTime"));
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn, ps, rs);
}
return user;
}
/**
* 查询全部数据,返回值是List集合,集合中是全部用户数据
*/
public List<User> findAll() {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
ArrayList<User> list = new ArrayList<>( );
try {
ps = conn.prepareStatement("select * from tb_user");
rs = ps.executeQuery( );
while (rs.next( )) {
// 每一行数据都需要创建新对象
user = new User( );
user.setId(rs.getInt("id"));
user.setPhone(rs.getString("phone"));
user.setUsername(rs.getString("username"));
user.setSex(rs.getInt("sex"));
user.setMoney(rs.getDouble("money"));
user.setPassword(rs.getString("password"));
user.setCreateTime(rs.getDate("createTime"));
// 将每个对象装入集合
list.add(user);
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn, ps, rs);
}
return list;
}
/**
* 插入数据
*/
public boolean addUser(User user) {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
int num = 0;
try {
String sql = "insert into tb_user (id,username,password,phone,createTime,money,sex) values (?,?,?,?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, user.getId( ));
ps.setString(2, user.getUsername( ));
ps.setString(3, user.getPassword( ));
ps.setString(4, user.getPhone( ));
ps.setDate(5, new java.sql.Date(user.getCreateTime( ).getTime( )));
ps.setDouble(6, user.getMoney( ));
ps.setInt(7, user.getSex( ));
num = ps.executeUpdate( );
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn, ps);
}
return num > 0 ? true : false;
}
/**
* 删除数据
*/
public boolean deleteById(int id) {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
int num = 0;
try {
String sql = "delete from tb_user where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
num = ps.executeUpdate( );
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn, ps);
}
return num > 0 ? true : false;
}
/**
* 根据id更新用户
* 更新user表的数据,根据User对象的属性更新,
* 如果属性值是null则不更新,不为null就更新对应的字段
* ---------------------------------------
* update tb_user set username= ?,password = ?,phone = ? ,,,,
* -------------------------------------------
* 因为值为空不为空情况不一样,那么每次更新的sql语句也不太一样,
* 解决方案
* 1) 方案一: (更新全部字段)
* 在传给updateById()的user对象中,属性全部赋值,没有null
* 属性值跟原表一样的,更新了也跟原表没变化
* 属性值跟原表不一样的,就会更新成新值
* 2) 方案二: (更新部分字段)
* 根据传给updateById()的user对象中的属性值判断
* 属性有值,就更新,属性没有值就不更新
* 即SQL语句要动态拼接
*/
// 方案一
public boolean updateById1(User user) {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
int num = 0;
try {
String sql = "update tb_user set username = ?,password = ?," +
"phone = ?,createTime = ?,money = ? ," +
"sex = ? where id = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getUsername( ));
ps.setString(2, user.getPassword( ));
ps.setString(3, user.getPhone( ));
ps.setDate(4, new java.sql.Date(user.getCreateTime( ).getTime( )));
ps.setDouble(5, user.getMoney( ));
ps.setInt(6, user.getSex( ));
ps.setInt(7, user.getId( ));
num = ps.executeUpdate( );
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn, ps);
}
return num > 0 ? true : false;
}
// 方案二
public boolean updateById2(User user) {
Connection conn = DBUtil.getConnection( );
Statement statement = null;
int num = 0;
try {
String sql = "update tb_user set ";
if (user.getUsername( ) != null) {
sql += "username = '" + user.getUsername( ) + "',";
}
if (user.getPassword( ) != null) {
sql += "password = '" + user.getPassword( ) + "' ,";
}
if (user.getPhone( ) != null) {
sql += "phone = '" + user.getPhone( ) + "' ,";
}
// ...继续拼接其他字段,我在此省略
System.out.println("字段拼接完的sql " + sql);
// update tb_user set username= 'aaa',password = '111',
// 截取最后一个,
int index = sql.lastIndexOf(",");
String newSql = sql.substring(0, index);
System.out.println("截取,后的sql " + newSql);
// 最后拼接条件
newSql += " where id = " + user.getId( );
System.out.println("最终的sql " + newSql);
statement = conn.createStatement( );
num = statement.executeUpdate(newSql);
} catch (Exception e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn,statement);
}
return num > 0 ? true : false;
}
}
测试
package com.qf.test;
import com.qf.dao.UserDao;
import com.qf.model.User;
import java.util.Date;
import java.util.List;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class TestUserDao {
public static void main(String[] args) {
UserDao userDao = new UserDao( );
// User user = userDao.findUserById(2);
// System.out.println(user );
// List<User> list = userDao.findAll( );
// list.forEach(e -> System.out.println(e ));
// User user = new User( );
// user.setId(8);
// user.setUsername("博文");
// user.setPassword("123456");
// user.setCreateTime(new Date());
// boolean isOk = userDao.addUser(user);
// System.out.println("插入是否成功?" + isOk );
// boolean isOk = userDao.deleteById(4);
// System.out.println("删除是否成功 " + isOk );
// User user = new User( );
// user.setId(1);
// user.setUsername("aaaa");
// user.setPassword("123456");
// user.setPhone("11110000");
// user.setMoney(1000);
// user.setSex(1);
// user.setCreateTime(new Date(122,10,21));
// boolean isOk = userDao.updateById1(user);
// System.out.println(isOk );
User user = new User( );
user.setId(2);
user.setUsername("ROOT");
user.setPassword("654321");
boolean isOk = userDao.updateById2(user);
System.out.println(isOk );
}
}
小结
1 DAO是什么
2 关于数据库操作的类,应该放在什么包,什么类中
3 要将数据封装成对象(ORM)
4 学会在编码时通过输出语句确定结果是否符合预期
5 学会DEBUG
6 记住以上5个CRUD思路
2 数据库连接池
目前数据库连接是使用是建立连接,用完直接关闭连接.即需要不断创建和销毁连接.就会消耗系统资源.借鉴线程池的思想,设计出
数据库连接池
.在程序初始时,预先创建好指定数量的数据库连接对象,存储连接池。需要用时就去取,用完就放回去即可。就会不会频繁创建和销毁,从而节省系统资源。
使用上的线程池有很多
- druid (德鲁伊)
- c3p0
- dbcp
- …
2.1 Druid数据库连接池
Druid是阿里开源技术,性能很好
使用步骤
导入依赖druid.jar包
创建一个jdbc.properties
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/java2217?useSSL=false username=root password=123456 # ----- 加入druid的一些连接配置 #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 --> maxWait=5000
修改之前的DBUtil
public class DBUtil { // 创建Properties类对象,专用于操作properties文件 private static final Properties properties = new Properties(); // 声明Druid连接池的连接池对象 // 数据连接,一般称作数据源 dataSource private static DruidDataSource dataSource;
static { try { InputStream inputStream = DBUtil.class.getResourceAsStream("/jdbc.properties"); properties.load(inputStream); // 不需要由我们加载驱动 // 需要给dataSource赋值 dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { System.out.println("加载驱动异常!!" ); e.printStackTrace( ); } } public static Connection getConnection() { Connection conn = null; try{ // 不需要我们获得连接 // 而是通过Druid获得 conn = dataSource.getConnection(); } catch (Exception e) { System.out.println("获得连接出异常!!!" ); e.printStackTrace(); } return conn; } // 后续正常...跟之前一样
}
开始使用
跟之前一样使用
3 反射(reflect)
JAVA反射机制是在
运行状态
中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对 象,都能够调用它的任意一个方法和属性这种
动态获取的信息
以及动态调用对象的方法
的功能称为java 语言的反射机制。反射是在程序运行过程中拿到类的字节码文件,进而获得类中的属性,方法等.
3.1 获得类的字节码文件
- Object类的方法 getClass()
- 类的静态属性 Xxx.class
- Class类的静态方法Class.forName(“xxx”)
/**
* 演示获取字节码文件
*/
public static void getClassFile() throws ClassNotFoundException {
// 方式1
Class<?> clazz = Class.forName("com.qf.model.User");
// 方式2
Class<User> clazz2 = User.class;
// 方式3
User user = new User( );
Class<? extends User> clazz3 = user.getClass( );
if (clazz.equals(clazz2) && clazz2.equals(clazz3)) {
System.out.println("是同一个字节码文件" );
} else {
System.out.println("不是" );
}
}
3.2 获得并设置属性(Field)
/**
* 获得字节码中的属性并操作
*/
public static void getAndSetFields() throws Exception {
Class<User> clazz = User.class;
/**
* Filed getField(String name)
* 通过属性名获得属性对象(只能获得public修饰的属性的属性对象)
* Filed getDeclaredField()
* 通过属性名获得属性对象(获得任意修饰符修饰的属性对象)
*/
// Field id = clazz.getField("id");
// System.out.println(id );
Field id1 = clazz.getDeclaredField("id");
System.out.println(id1 );
/**
* Filed[] getFields( ) 获得public修饰所有属性对象,返回数组
* Filed[] getDeclaredFields( ) 获得所有属性对象,返回数组
*/
Field[] fields = clazz.getFields( );
System.out.println(fields.length );
Field[] declaredFields = clazz.getDeclaredFields( );
System.out.println(declaredFields.length );
// =============================================
// 获得属性名
String name = id1.getName( );
System.out.println("name = "+ name );
// 获得属性访问修饰符
int modifiers = id1.getModifiers( );
System.out.println("访问修饰符: " + modifiers );
// 获得属性值
// 获得哪个对象的该属性值
// 但是不能获得私有属性的值
// 可以通过设置,就可以访问,
id1.setAccessible(true);
User user = new User( );
int value = id1.getInt(user);
System.out.println("id = " + value );
// 设置属性值
// 设置哪个对象的该属性值
id1.setInt( user,11);
System.out.println(user );
}
3.3 获得并设置方法(Method)
/**
* 获得字节码中的方法
*/
public static void getAndSeMethod() throws Exception {
Class<User> clazz = User.class;
// 方法有重载,需要通过参数来确定获得哪个方法
Method m1 = clazz.getMethod("m1"); // 获得无参的m1方法
Method m1_ = clazz.getMethod("m1",int.class); // 获得有参的m1(int)方法
// 获得关于方法的所有信息
int count = m1.getParameterCount( );// 参数个数
int count_ = m1_.getParameterCount( );// 参数个数
// 操作方法,让方法执行
// 参数1: 哪个对象的该方法执行
// 参数2: 该方法执行时的参数
Object ret = m1.invoke(new User( ));
System.out.println("m1()执行后的返回值:" + ret );
m1_.invoke(new User(),222);
}
3.4 获得并设置构造方法(Constructor)
/**
* 获得字节码中的构造方法
*/
public static void getAndSeConstructor() throws Exception {
Class<User> clazz = User.class;
// 通过参数来获得有参还是无参构造
Constructor<User> constructor = clazz.getConstructor( );
// 构造方法执行,创建对象
User user = constructor.newInstance( );
System.out.println(user );
// 创建字节码的对象,还有另外方法
// 可以通过字节码,直接创建
User user1 = clazz.newInstance( );
}
4 使用反射封装DBUtil
package com.qf.util;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class DBUtil {
// 创建Properties类对象,专用于操作properties文件
private static final Properties properties = new Properties( );
// 声明Druid连接池的连接池对象
// 数据连接,一般称作数据源 dataSource
private static DruidDataSource dataSource;
static {
try {
InputStream inputStream = DBUtil.class.getResourceAsStream("/jdbc.properties");
properties.load(inputStream);
// 不需要由我们加载驱动
// 需要给dataSource赋值
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
System.out.println("加载驱动异常!!");
e.printStackTrace( );
}
}
public static Connection getConnection() {
Connection conn = null;
try {
// 不需要我们获得连接
// 而是通过Druid获得
conn = dataSource.getConnection( );
} catch (Exception e) {
System.out.println("获得连接出异常!!!");
e.printStackTrace( );
}
return conn;
}
/**
* 关闭所有流
*/
public static void closeAll(Connection conn, Statement s) {
if (conn != null) {
try {
conn.close( );
} catch (SQLException throwables) {
throwables.printStackTrace( );
}
}
if (s != null) {
try {
s.close( );
} catch (SQLException throwables) {
throwables.printStackTrace( );
}
}
}
public static void closeAll(Connection conn, Statement s, ResultSet rs) {
if (conn != null) {
try {
conn.close( );
} catch (SQLException throwables) {
throwables.printStackTrace( );
}
}
if (s != null) {
try {
s.close( );
} catch (SQLException throwables) {
throwables.printStackTrace( );
}
}
if (rs != null) {
try {
rs.close( );
} catch (SQLException throwables) {
throwables.printStackTrace( );
}
}
}
/**
* 封装查询方法,返回一个对象
* 参数1 执行查询的SQL,预处理的,条件用?占位
* select * from tb_user where id = ? and username = ? and password = ?
* 参数2 结果要封装的类
* 参数3 给?赋值,不定长参数,是数组
* 1,admin,123456
*/
public static <T> T selectOne(String sql, Class<T> t, Object... args) {
Connection conn = getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
T target = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery( );
/**
* 创建对象
* 从数据库取出数据,并设置对象属性
*/
while (rs.next( )) {
target = t.newInstance( );
Field[] declaredFields = t.getDeclaredFields( );
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
Object value = rs.getObject(field.getName( ));
// 破解私有
field.setAccessible(true);
// 给对象的该字段赋值
field.set(target, value);
}
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
closeAll(conn, ps, rs);
}
return target;
}
public static <T> List<T> selectAll(String sql, Class<T> t, Object... args) {
Connection conn = getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
T target = null;
ArrayList<T> list = new ArrayList<>( );
try {
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery( );
/**
* 创建对象
* 从数据库取出数据,并设置对象属性
*/
while (rs.next( )) {
target = t.newInstance( );
Field[] declaredFields = t.getDeclaredFields( );
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
Object value = rs.getObject(field.getName( ));
// 破解私有
field.setAccessible(true);
// 给对象的该字段赋值
field.set(target, value);
}
list.add(target);
}
} catch (Exception e) {
e.printStackTrace( );
} finally {
closeAll(conn, ps, rs);
}
return list;
}
/**
* 增删改方法一样
*/
public static boolean update(String sql, Object... args) {
Connection conn = getConnection( );
PreparedStatement ps = null;
int num = 0;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
num = ps.executeUpdate( );
} catch (Exception e) {
e.printStackTrace( );
} finally {
closeAll(conn, ps);
}
return num > 0 ? true : false;
}
}
5 作业
1 上午5道题,按照格式(包名,类名,到封装)写一遍
2 重复一遍反射代码
3 封装DBUtil理解
4 DBUtil再次写那5道题
5 昨天sql题,写个2道
finally {
closeAll(conn, ps, rs);
}
return list;
}
/**
* 增删改方法一样
*/
public static boolean update(String sql, Object... args) {
Connection conn = getConnection( );
PreparedStatement ps = null;
int num = 0;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
num = ps.executeUpdate( );
} catch (Exception e) {
e.printStackTrace( );
} finally {
closeAll(conn, ps);
}
return num > 0 ? true : false;
}
}
# 5 作业
1 上午5道题,按照格式(包名,类名,到封装)写一遍
2 重复一遍反射代码
3 封装DBUtil理解
4 DBUtil再次写那5道题
5 昨天sql题,写个2道