前言:
以前我学习项目都没有用到过数据库,都是用XML代替,
学习数据库也是直接学习其中的语法。
现在终于将二者连接在一起了。
也遇到了点小坑,刚好解决了来分享一波。
开发步骤:
1.建项目,引入数据库驱动包
2.加载驱动
Class.forName(..);
3.获取连接对象
4.创建执行sql语句的stmt对象; 写sql
5.执行sql
a)更新 delete/insert/update
i.executeUpdate();
b)查询 select
i.executeQuery();
6.关闭/异常
组成JDBC的2个包:内置了可以不用管
java.sql
javax.sql
首先需要到mysql官网下载mysql的java的驱动程序包
我使用的是:mysql-connector-java-8.0.11
解压后将
mysql-connector-java-8.0.11.jar放入创建好的lib空文件夹下
准备工作完毕,正式进入操作。
注意:用到的都是接口,所以导入包的时候别导错了。
因为驱动程序不只是mysql,也可以换成sqlserver,所以需要接口,而不是具体的类。
注册驱动程序:
Class.forName(driverClass);//注册驱动程序
获取连接:
Connection cnn = DriverManager.getConnection(url,
user, password);
获取操作对象(可以理解为把用java写好的sql语句运输到数据库执行)
//创建Statement对象 用于执行静态的sql语句
1. Statement createStatement()
//创建PreparedStatement对象 用于执行预编译sql语句
2. PreparedStatement prepareStatement(String sql)
//创建CallableStatement对象 用于执行存储过程的sql语句
3. CallableStatement prepareCall(String sql)
一般都是用:PreparedStatement
而不是Statement
PreparedStatement vs Statment
1)语法不同:PreparedStatement可以使用预编译的sql,而Statment只能使用静态的sql
2)效率不同: PreparedStatement可以使用sql缓存区,效率比Statment高
3)安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
sql注入典型代码:
’ OR 1=1 –
执行操作:
//用于更新数据
int executeUpdate()
//用于查询数据
ResultSet executeQuery()
//允许执行查询语句、更新语句、DDL语句。
boolean execute()
int executeUpdate()
执行给定 SQL 语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,
或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。
·返回值是更新的记录数量·
ResultSet executeQuery()
执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。
boolean execute()
返回值为true时,表示执行的是查询语句,可以通过getResultSet方法
获取结果;返回值为false时,执行的是更新语句或DDL语句,
getUpdateCount方法获取更新的记录数量。
execute是executeUpdate与executeQuery的综合
返回数据:
CallableStatement接口下的ResultSet executeQuery()
获取数据:
ResultSet接口:用于封装查询出来的数据
boolean next() : 将光标移动到下一行
getXX() : 获取列的值
我创建了一个工具类来获取jdbc和关闭jdbc:
public class jdbcUtil {
private static String url = null;
private static String user = null;
private static String password = null;
private static String driverClass = null;
static {
try {
Properties properties = new Properties();
InputStream in = jdbcUtil.class.getResourceAsStream("/db.properties");
properties.load(in);
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driverClass = properties.getProperty("driverClass");
Class.forName(driverClass);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("驱程程序注册出错");
}
}
public static Connection getConnection() {
try {
Connection cnn = DriverManager.getConnection(url, user, password);
return cnn;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void close(Connection cnn, Statement st) {
if (st != null) {
try {
st.close();
st = null;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (cnn != null) {
try {
cnn.close();
cnn = null;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
public static void close(Connection cnn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (st != null) {
try {
st.close();
rs = null;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (cnn != null) {
try {
cnn.close();
cnn = null;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
问题1:
getResourceAsStream 用法大致有以下几种:
第一: 要加载的文件和.class文件在同一目录下,例如:com.x.y 下有类me.class ,同时有资源文件myfile.xml 那么,应该有如下代码: Java代码 me.class.getResourceAsStream("myfile.xml");
第二:在me.class目录的子目录下,例如:com.x.y 下有类me.class ,同时在 com.x.y.file 目录下有资源文件myfile.xml 那么,应该有如下代码: Java代码 me.class.getResourceAsStream("file/myfile.xml");
第三:不在me.class目录下,也不在子目录下,例如:com.x.y 下有类me.class ,同时在 com.x.file 目录下有资源文件myfile.xml 那么,应该有如下代码: Java代码 me.class.getResourceAsStream("/com/x/file/myfile.xml");
问题2:
Class.forName(driverClass);
Class.forName方法的作用,就是初始化给定的类.而我们给定的MySQL的Driver类中,它在静态代码块中通过JDBC的DriverManager注册了一下驱动.我们也可以直接使用JDBC的驱动管理器注册mysql驱动.从而代替使用Class.forName.
Class.forName(xxx.xx.xx) 返回的是一个类
Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
又创建了一个properties目的是为了好随时替换账号密码和mysql也算是解耦了。
url=jdbc:mysql://localhost:3306/day15?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
user=root
password=123456
driverClass=com.mysql.cj.jdbc.Driver
如果你单纯写:
Connection conn = DriverManager.getConnection("jdbc
:mysql://localhost:3306/scott",
"root", "123456");
是会报错的
因为高版本的mysql有两个问题:
1. MySQL在高版本需要指明是否进行SSL连接 数据加密
2. JDBC连接Mysql6 com.mysql.cj.jdbc.Driver,
需要指定时区serverTimezone:
MySQL jdbc 6.0 版本以上必须配置以下参数:
useSSL=false
serverTimezone=Asia/Shanghai或者Asia/Hongkong
参考:
https://blog.youkuaiyun.com/superdangbo/article/details/78732700
连接问题解决了
现在再来看看操作问题。
public static void Insert() {
Connection cn = null;
PreparedStatement pStatement = null;
try {
cn = jdbcUtil.getConnection();
String sql = "INSERT INTO teacher value(?,?)";
pStatement = cn.prepareStatement(sql);
pStatement.setInt(1, 22);
pStatement.setString(2, "我爱你");
int count = pStatement.executeUpdate();
System.out.println("影响了几行:" + count);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
} finally {
jdbcUtil.close(cn, pStatement);
}
}
public static void update() {
Connection cn = null;
PreparedStatement pStatement = null;
try {
cn = jdbcUtil.getConnection();
String sql = "UPDATE TEACHER SET name=? where id=?";
pStatement = cn.prepareStatement(sql);
pStatement.setInt(2, 22);
pStatement.setString(1, "sdf");
int count = pStatement.executeUpdate();
System.out.println("影响了几行:" + count);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
} finally {
jdbcUtil.close(cn, pStatement);
}
}
public static void drop() {
Connection cnn = null;
PreparedStatement pStatement = null;
try {
cnn = jdbcUtil.getConnection();
String sql = "DELETE FROM teacher where id=?";
pStatement = cnn.prepareStatement(sql);
pStatement.setInt(1, 22);
int count = pStatement.executeUpdate();
System.out.println("影响了几行:" + count);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
} finally {
jdbcUtil.close(cnn, pStatement);
}
}
public static void select() {
Connection cnn = null;
PreparedStatement pStatement = null;
ResultSet rs = null;
try {
cnn = jdbcUtil.getConnection();
String sql = "SELECT *FROM teacher";
pStatement = cnn.prepareStatement(sql);
rs = pStatement.executeQuery();
while (rs.next()) {
int id = rs.getInt("ID");
String name = rs.getString("Name");// 不区分大小写
System.out.println("id:" + id + " " + "name:" + name);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
} finally {
jdbcUtil.close(cnn, pStatement, rs);
}
}
存储过程:
没有参数
两者都可以执行存储过程:
resultSet=callableStatement.executeQuery();//如果没有返回值就不需要接收
callableStatement.executeLargeUpdate(); //返回操作的行数
public static void Noparam() {
Connection connection=null;
CallableStatement callableStatement=null;
ResultSet resultSet=null;
try {
connection = jdbcUtil.getConnection();
callableStatement = connection.prepareCall("CALL hah2()");
callableStatement.executeQuery();
while(resultSet.next()) {
int number=resultSet.getInt("id");
String name=resultSet.getString("Name");
System.out.println(number+" "+name);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}finally {
jdbcUtil.close(connection, callableStatement, resultSet);
}
}
有输入参数
public static void inParam() {
Connection connection=null;
CallableStatement callableStatement=null;
ResultSet resultSet=null;
try {
connection = jdbcUtil.getConnection();
callableStatement = connection.prepareCall("CALL hah1(?)");
callableStatement.setInt(1, 2);
resultSet=callableStatement.executeQuery();
while(resultSet.next()) {
int number=resultSet.getInt("id");
String name=resultSet.getString("Name");
System.out.println(number+" "+name);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}finally {
jdbcUtil.close(connection, callableStatement, resultSet);
}
}
有输出参数
public static void outParam() {
Connection connection=null;
CallableStatement callableStatement=null;
ResultSet resultSet=null;
try {
connection = jdbcUtil.getConnection();
//参数二: 存储过程中的输出参数的jdbc类型 VARCHAR(20)
callableStatement = connection.prepareCall("CALL hah3(?,?)");
//设置输入参数
callableStatement.setInt(1, 4);
callableStatement.registerOutParameter(2, java.sql.Types.VARCHAR);
callableStatement.executeQuery();//结果不是返回到结果集中,而是返回到输出参数中
String name=callableStatement.getString(2);//getXX方法专门用于获取存储过程中的输出参数
System.out.println(name);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}finally {
jdbcUtil.close(connection, callableStatement, resultSet);
}
}
registerOutParameter:设置返回值的类型