什么是JDBC?
java连接数据库,可以使用java语言连接数据库完成CRUD(增删改查)操作
权限定名 | 描述 |
---|---|
java.sql.DriverManager | 管理多个数据库类,获取数据库连接方法 |
java.sql.Connection | 代表一个数据库已连接 |
java.sql.Statement | 发送SQL语句到数据库工具 |
java.sql.ResultSet | 保存SQL查询语句的结果数据 |
java.sql.SQLException | 处理数据库应用程序发生的异常 |
JDBC开发步骤
1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");(8.0版本的使用)
2.连接数据库
通过DriverManager.getConnection(url,user,password)获取数据库对象;
url:"jdbc:mysql://localhost:3306/companydb?characterEncoding=utf-8&serverTimezone=UTC"(8.0版本)
user:"root"
password:"131556"
3.获取发送SQL的对象
通过connection对象获取statement对象,用于对数据库进行访问
Statement statement=connection.createStatement()
4.执行SQL语句,并获得返回值
String sql ="insert into t_jobs(job_id,job_title,min_salary,max_salary)values('Java_hh','Java_ff',4000,10000)";
int result = statement.executeUpdate(sql);
在编写DML语句时,一定要注意字符串参数的符号是单引号’值’;
DML:增删改时,返回受影响行数(int类型)
DQL:查询时返回的是(ResultSet集)
5.处理结果
if(result==1)
{
System.out.println("success");
}
else
{
System.out.println("fail");
}
6.释放资源,先开后关
statement.close();
connection.close();
先引用哪个函数就最后关哪个
ResultSet结果集
在执行查询SQL后,存放查询到的结果集数据
1.接受结果集
ResultSet resultSet = statement.executeQuery(sql);
2.遍历resultset的数据
while(resultSet.next())
{
String job_id = resultSet.getString(1);
String job_title= resultSet.getString(2);
String min_salary = resultSet.getString(3);
String max_salary = resultSet.getString(4);
System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
}
PreparedStatement
案例之用户登入
public static void main(String[] args)throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password=scanner.next();
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?characterEncoding=utf-8&serverTimezone=UTC", "root", "131556");
Statement statement = connection.createStatement();
ResultSet resultSet=statement.executeQuery("select * from user where username='"+username+"'and password ='"+password+"' " );
if(resultSet.next())
{
System.out.println("登入成功");
}
else
{
System.out.println("登入失败");
}
resultSet.close();
statement.close();
connection.close();
}
但是在这里有个问题,如果我输入的username带有一个#,那么在sql语句中表示#后面的为注释语句,只会判断前面的语句是否正确,所以后面的语句将不起作用,所以这时你随便输入一个密码都可以登录成功,这就是sql注入问题,要解决该问题就要使用到PreparedStatement。
PreparedStatement 的作用
1.预编译sql语句,效率高
2.安全,避免sql注入问题
3.可以动态的填充数据,执行多个的同构sql语句
PreparedStatement preparedStatement=connection.prepareStatement("select * from user where username=? and password =?; " );
//为?占位符赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//执行sql语句,获得结果
ResultSet resultSet = preparedStatement.executeQuery();
封装工具类
我们在JDBC的使用中会存在多次重复使用的代码,我们可以编写一个通用的封装工具类,以后连接数据库,释放资源都可以使用这个工具类。
package com.feng;
import java.sql.*;
public class DBUtils {//类加载,只执行一次
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//1.获得连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?characterEncoding=utf-8&serverTimezone=UTC","root","131556");
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//2.释放资源
public static void closeAll(Connection connection,Statement statement, ResultSet resultSet){
try {
if(resultSet!=null)
{
resultSet.close();
}
if(statement!=null)
{
statement.close();
}
if(connection!=null)
{
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//这个是之前的代码引用封装工具类
Connection connection = DBUtils.getConnection();
DBUtils.closeAll(connection,statement,resultSet);
try…catch…是抛出异常,快捷键是 ctrl+alt+T。
跨平台方案
在用户需要改变自己信息数据时通过跨平台方案修改,而不用改动class文件。
//properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/companydb?characterEncoding=utf-8&serverTimezone=UTC
username=root
password=131556
public class DBUtils {
private static final Properties PROPERTIES=new Properties();//存储配置文件的map
static{
InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
try {
PROPERTIES.load(is);//通过流,将配置文件的内容加载到properties集合
Class.forName(PROPERTIES.getProperty("driver"));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection = null;
try {
connection= DriverManager.getConnection(PROPERTIES.getProperty("url"),PROPERTIES.getProperty("username"),PROPERTIES.getProperty("password"));
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
ORM
从数据库查询到的结果都是零散的,orm就是帮我们把零散的数据进行封装整理。
实体类(entity):零散数据的载体
* 一行数据中,多个零散的数据进行整理
* 通过entity的规则对表的数据进行对象的封装
* 表名=类名 列名=属性名 ;提供各个属性的get,set方法
* 提供无参构造方法
String job_id=resultSet.getString("job_id");
String job_title=resultSet.getString("job_title");
String min_salary=resultSet.getString("min_salary");
String max_salary=resultSet.getString("max_salary");
//创建对象,封装查询到的零散数据
T_jobs t_jobs = new T_jobs();
t_jobs.setJob_id(job_id);
t_jobs.setJob_title(job_title);
t_jobs.setMin_salary(min_salary);
t_jobs.setMax_salary(max_salary);
System.out.println(t_jobs);
//每遍历一次得到的对象,存放在集合中,方便后续使用
t_jobsList.add(t_jobs);
for (T_jobs t:t_jobsList) {
System.out.println(t);
DAO数据访问对象
DAO实现了业务逻辑与数据库分离,
对同一张表的操作封装在XxxxDaoImpl对象中,
根据增删改查实现不同的方法
首先是在在mysql中创建数据库,然后创建一个实体类(entity)文件,然后创建DaoImpl文件各种操作都在该文件中,再创建一个总的将他们整合在一起的文件
Date工具类
数据库存储的日期是java.sql.Date 类型,但java存储的日期是java.util.Date类型。当我们将java中的日期导入到数据库中需要转换。
java.sql.Date
*不可以通过字符串创建对应的时间对象,只能通过毫秒值创建对象
*可以通过JDBC直接插入到数据库
java.util.Date
java应用层面的的日期类型,可以通过字符串创建对象
无法通过JDBC插入数据库
SimpleDateFormat
格式化和解析日期的具体类,。允许进行格式化(日期->文本),解析(文本->日期)和规范化。
将java中字符串的日期转化为数据库类型
String str="1999-09-09";
SimpleDateFormat sdf= new SimpleDateFormat("yyy-MM-dd");
java.util.Date date=sdf.parse(str);
//sql.date不支持字符串转换,只支持毫秒值
//通过util.date拿到毫秒值,再转换成sql.date
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
System.out.println(sqlDate);
}
将字符串转换为java.util.Date类型
String str="1999-09-09";
SimpleDateFormat sdf= new SimpleDateFormat("yyy-MM-dd");
java.util.Date date=sdf.parse(str);
System.out.println(date);
将util.Date转换为字符串形式
String dates=sdf.format(new java.util.Date());
System.out.println(dates);
sql.Date是util.Date的子类,父类的对象可以引用子类。
在我们使用java向数据库插入日期就需要先将字符串转化为util类型,再将util类型转换为sql类型。可以将转换代码封装在一个工具类中,等需要时再调用。
什么是业务
代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成(软件提供的一个功能都叫业务)
大概流程是先在数据库创建一张表,在Java创建一个工具类可以用来调用java与数据库的连接和关闭,再创建一个实体类封装数据库表的信息,再创建DAO进行对数据库的各项操作,再创建一个业务需要的流程的类,最后测试。
这是个用户转账的例子
我们需要注意的是当功能出现问题时,我们需要对业务进行管控,令程序回到最初的状态。但是因为当我们用选择或者更新操作或者功能操作时调用的connection不是同一个,这样即便编写了提交与回滚操作在本人账户转账成功后会扣钱但对方的账户并不会加钱,因此我们面对程序出现问题需要做的便是令他们调用的是同一个connection,这个方法是ThreadLocal。
ThreadLocal
可以将整个线程中,存储一个共享值。
线程拥有一个类似Map的属性,键值对结果<ThreadLocal对象,值>
一个线程共享一个ThreadLocal,在整个流程中可以任意环节存值或取值。
DBUtils封装事务
在用户服务操作中,事务的开启,提交,回滚不应该在该文件中出现通过用户服务界面改变数据,这个时候我们可以将事务的开启,提交,回滚封装在DBUtils中,再在用户服务界面调用它,另外需注意的是当我们完成服务的时候关闭connection连接时我们需要将connection对象从threadLocal中移除——>threadLocal.remove();