JDBC创建的方式
1、加载驱动通过配置文件
2、连接数据库获取连接对象
3、Statement/PrepareStatement获得预编译对象
4、执行sql语句得到结果集
5、处理结果集
6、关闭资源
配置文件
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.116.1:3306/lee?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
user=root
password=123456
//注册驱动加载驱动获取连接对象
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
案例
@Override
public int in(String inName,Double money) {
Connection conn=null;
PreparedStatement ps=null;
try {
connection.setAutoCommit(false);
//获取连接将获取连接封装在DBUtils的getConnection方法里
conn = DBUtils.getConnection();
//预编译
ps= conn.prepareStatement("update t_account set balance=balance+? where name=?");
//给占位符赋值
ps.setObject(1,money);
ps.setObject(2,inName);
//执行sql语句得到结果
int i = ps.executeUpdate();
//处理结果集
return i;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
//关闭资源,将关闭资源封装在DBUtils的close方法里
DBUtils.closeAuto(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
}
PrepareStatement和Statement的区别
prepareStatement进行预编译处理,重要参数用占位符?代替,预编译之后再给占位符赋值,可以防止SQL注入问题
//预编译
ps= conn.prepareStatement("update t_account set balance=balance+? where name=?");
//给占位符赋值
ps.setObject(1,money);
ps.setObject(2,inName);
封装工具类
将注册加载驱动获取连接和关闭连接释放资源封装起来
public class DBUtils {
//增加threadlocal
//一个线程对应一个ThreadLocal
//public static DruidDataSource druidDS;
//ThreadLocal的作用是保证一个线程里获取的连接都是同一个连接,用于处理事务
public static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
public static Connection getConnection() throws Exception {
if (threadLocal.get()==null){
Properties p=new Properties();
p.load(new FileInputStream("u.properties"));
// druidDS= (DruidDataSource) DruidDataSourceFactory.createDataSource(p);
// DruidPooledConnection connection1 = druidDS.getConnection();
//加载注册驱动获取连接
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
//将获取的连接放入ThreadLocal里
threadLocal.set(connection1);
}else {
return threadLocal.get();
}
return threadLocal.get();
}
public static void closeAuto(AutoCloseable...cs) throws Exception {
for (AutoCloseable cc: cs) {
//判断是否为空,不为空就释放
if (cc!=null){
//判断资源是否是connection,如果是,则先从threadlocal里移除再关闭资源
if (!(cc instanceof Connection)){
cc.close();
}else {
threadLocal.remove();
cc.close();
}
}
}
}
//获取连接的方式一
Properties p=new Properties();
p.load(new FileInputStream("u.properties"));
//------------------------------------------
//获取连接的方式二
Properties p=new Properties();
InputStream resourceAsStream = DBUtils.class.getResourceAsStream("/db.properties");
p.load(resourceAsStream);
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
ORM思想
下载Lombok插件并且导jar包可以自动写入get、set、构造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String name;
private String password;
private double balance;
}
将处理的结果集进行封装,封装到实体类对象中
@Override
public Account selectAccountByName(String name) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet resultSet=null;
try {
conn=DBUtils.getConnection();
ps=conn.prepareStatement("select * from t_account where name=?");
ps.setObject(1,name);
resultSet = ps.executeQuery();
if (resultSet.next()){
/**对获取的结果集进行封装,返回对象*/
int selectId=resultSet.getInt("id");
String selectName=resultSet.getString("name");
String selectPassword=resultSet.getString("password");
Double selectBalance=resultSet.getDouble("balance");
Account account=new Account(selectId,selectName,selectPassword,selectBalance);
return account;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(resultSet,ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
架构
View层(视图层)->Controller层(控制业务逻辑)->Service层(业务逻辑实现)->Dao层(和数据库进行交互)->DB(数据库)
Date工具类
主要是对java.util的时间和java.sql的时间进行转换
Date工具类
主要是对java.util的时间和java.sql的时间进行转换
@Override
public User Together(Scanner input) throws ParseException {
User user=new User();
System.out.println("请输入密码");
String password=input.next();
System.out.println("请输入年龄");
int age=input.nextInt();
System.out.println("请输入性别");
int sex=input.nextInt();
System.out.println("请输入生日");
String birthday=input.next();
//将字符串转换成util的Date存到对象中
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date birthdayData = simpleDateFormat.parse(birthday);
user.setBirthday(birthdayData);
user.setPassword(password);
user.setAge(age);
user.setSex(sex);
return user;
}
}
@Override
public int add(String name) throws ParseException {
Connection conn=null;
PreparedStatement ps=null;
User user = selectUserByName(name);
if (user==null){
if (name.equals("null")||name.length()==0){
return 0;
}
user=Together(input);
user.setName(name);
try {
conn = DBUtils.getConnection();
ps=conn.prepareStatement("insert into t_user(username,password,age,sex,birthday) values (?,?,?,?,?)");
ps.setObject(1,user.getName());
ps.setObject(2,user.getPassword());
ps.setObject(3,user.getAge());
ps.setObject(4,user.getSex());
//utilDate类型转换成sql的Date类型,Date(util)->long->Date(sql)
//对象中取出util的Date,转换成long型再转成sql的date型
ps.setObject(5,new Date(user.getBirthday().getTime()));
int i = ps.executeUpdate();
if (i>0){
return i;
}else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(ps,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
}else {
// System.out.println("此用户已经存在");
return 0;
}
return 0;
}
@Override
public ArrayList<User> selectAll() {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DBUtils.getConnection();
ps = conn.prepareStatement("select * from t_user");
rs=ps.executeQuery();
ArrayList<User> list=new ArrayList<>();
while(rs.next()){
//结果集得到的sql类型的date可以直接装箱成父类util的Date
String username = rs.getString("username");
String password = rs.getString("password");
int age = rs.getInt("age");
int sex = rs.getInt("sex");
Date birthday = rs.getDate("birthday");
User user=new User(username,password,age,sex,birthday);
list.add(user);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(rs,ps,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
事务的处理
事务的处理需要保证连接是同一个连接
方法一:连接作为参数
方法二:ThreadLocal的使用
事务的四个特性:
1、原子性 同时成功或者同时失败
2、一致性 一个加钱一个减钱
3、隔离性 事务之间不相互影响
4、持久性 对数据库一旦操作就永久影响
问题:
脏读 未提交就能读到
不可重复读 提交之后事务没重新打开就能读到
幻读
解决:设置隔离级别
Connection connection=null;
// try {
try {
//保证同一个连接
connection = DBUtils.getConnection();
//开启事务里面设置自动提交为false
DBUtils.start();
sql="update t_account set balance=balance+? where name=?";
//加钱
int in = dao.commonUpdate(sql, money,inName);
// int i= 10/0;
sql="update t_account set balance=balance-? where name=?";
//扣钱
int out = dao.commonUpdate(sql, money,name);
System.out.println("转账成功");
//封装事务提交
DBUtils.commit();
return 1;
} catch (Exception e) {
//封装事务回滚
DBUtils.rollback();
System.err.println("转账失败");
}finally {
try {
DBUtils.closeAuto(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
通用方法的修改
public int commonUpdate(String sql,Object...args){
Connection conn=null;
PreparedStatement ps=null;
try {
// connection.setAutoCommit(false);
conn = DBUtils.getConnection();
ps= conn.prepareStatement(sql);
for (int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
int i = ps.executeUpdate();
return i;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
}
@Override
public <T> ArrayList<T> commonSelect(String sql, Class<T> clazz, Object ...args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet resultSet=null;
ArrayList<T> list=null;
try {
conn=DBUtils.getConnection();
//"select * from t_account where name=?"
ps=conn.prepareStatement(sql);
for (int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//得到所有属性,前提所有字段和属性一致
Field[] declaredFields = clazz.getDeclaredFields();
resultSet = ps.executeQuery();
//方法二获得元数据
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()){
T t=clazz.newInstance();
list=new ArrayList<>();
//方法二
for (int i=0;i<columnCount;i++){
//得到结果集的值
Object value = resultSet.getObject(i + 1);
//得到元数据的列
String columnLabel = metaData.getColumnLabel(i + 1);
//通过字段列名得到对象对应属性
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t,value);
}
//通过反射得到所有属性,如果名字不对应会抓异常,通过读取配置文件的相应的属性名取获取数据库结果集的对应字段的值
// for (int i=0;i<declaredFields.length;i++){
// //得到对象属性的名字
// String fieldName = declaredFields[i].getName();
// declaredFields[i].setAccessible(true);
// Object value = resultSet.getObject("fieldName");
// declaredFields[i].set(t,value);
// }
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(resultSet,ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
配置文件
通过配置文件去配置连接
属性和字段名不一致
通过反射发现实体类属性和数据库字段名不一致时,通过配置文件去设置userName=user_name,相对应后再通过配置文件去得到结果集中的值
数据源DataSource连接池druid
原因:
数据库连接非常的耗费资源,连接创建后使用完就关闭,下一次连接又需要创建,耗费资源
好处:
使用连接池进行创建,可以只创建一次,然后从池中获取连接,节省服务器资源和时间
所有连接统一进行管理
连接池的使用:
导入jar包
创建配置文件注意配置文件的名字
初始化连接池
使用连接时直接从连接池中获取连接
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
username=root
password=123456
如果连接池的空闲连接大于最小连接,直接取连接操作接数
如果连接池中空闲连接小于最小连接数,自动增加连接,但不能大于最大连
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
<!如果连接池中空闲连接小于最小连接数,但是连接数量已经达到最大连接数,则让用户等待指定时间,在此时间内,若有连接返回到连接池中,则直接使用,如果没有连接返回池中,超过时间后报一个连接不够的异常>
maxWait=5000
public static DruidDataSource druidDS;
public static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
public static Connection getConnection() throws Exception {
if (threadLocal.get()==null){
Properties p=new Properties();
p.load(new FileInputStream("u.properties"));
druidDS= (DruidDataSource) DruidDataSourceFactory.createDataSource(p);
DruidPooledConnection connection1 = druidDS.getConnection();
// Class.forName(p.getProperty("driverClass"));
// String url=p.getProperty("url");
// String username=p.getProperty("user");
// String password=p.getProperty("password");
// Connection connection= DriverManager.getConnection(url,username,password);
threadLocal.set(connection1);
}else {
return threadLocal.get();
}
return threadLocal.get();
}
Apache封装的工具类
19.1.1 apache的DbUtils主要包含
事务控制需要自己控制
- ResultSetHandler接口:转换类型接口
- BeanHandler类:实现类,把一条记录转换成对象
- BeanListHandler类:实现类,把多条记录转换成List集合。
- ScalarHandler类:实现类,适合获取一行一列的数据。
- QueryRunner:执行sql语句的类
- 增、删、改:update();
- 查询:query();
- 实现:
导包
连接配置文件
//里面是个datasource
QueryRunner queryRunner=new QueryRunner(DBUtils.druidDS);
Account query = queryRunner.query("select * from t_account where name=?", new BeanHandler<Account>(Account.class), "ls");
System.out.println(query);
drud防止连接过多,设置定时关闭
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />