创建好项目后,到maven的仓库 Maven Repository: Search/Browse/Explore
搜索Mysql,选这个
进去后选一个版本然后复制下面的代码
然后复制到 pom.xml
然后点这个刷新下载maven
下载好后就会有这个
新建一个Servlet包,创建一个类并写入基本的Get代码
public class JdbcServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id=req.getParameter("id");
String sql="select * from users where id="+id;
}
}
完善注册数据库驱动代码
首先,导入数据库驱动jar包(以MySQL为例,mysql-connector-java-5.1.40-bin.jar),然后注册驱动
// 方法一.导致MySql驱动被注册两次,还导致程序和具体驱动类绑定,切换数据库,需要修改java源码重新编译,不建议使用!
DriverManager.registerDriver(new Driver());
// 方法二.驱动类的静态代码块已经注册驱动,只需要加载驱动类即可
// 用反射加载,与驱动类字符串绑定,字符串可放在配置文件,切换数据库,无需改源码重新编译,只需要修改配置文件!
Class.forName("com.mysql.jdbc.Driver");
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url,user,password);
url格式
MySql: jdbc:mysql://ip:3306/sid (本机地址简写jdbc:mysql:///sid)
Oracle: jdbc:oracle:thin:@ip:1521:sid
SqlServer: jdbc:microsoft:sqlserver://ip:1433;DatabaseName=sid
参数(可选,user和password可以写在url参数中)
?user=lioil&password=***&useUnicode=true&characterEncoding=UTF-8
协议:子协议://ip地址:端口号/库名?参数1=值&参数2=值
jdbc:mysql://localhost:3306/sid?useUnicode=true&characterEncoding=utf-8
@WebServlet(name = "jdbc",value = "/sql")
public class JdbcServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id=req.getParameter("id");
String sql="select * from users where id="+id;
String url="jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=utf-8";
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url,"root","123456");
System.out.println(connection);
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
String url="jdbc:mysql://localhost:3306/
mysql
?useUnicode=true&characterEncoding=utf-8" 是数据库名字
1.Statement常用方法:
单个执行SQL语句
boolean execute(String sql) 执行SQL语句,没有结果集返回false,有返回true(通过Statement.getResultSet获取结果集);
ResultSet executeQuery(String sql) 执行select语句,返回结果集
int executeUpdate(String sql) 执行insert delete update语句,返回影响行数
批量执行SQL语句(insert update delete)
void addBatch(String sql) 批量添加SQL语句(insert update delete)
int[] executeBatch() 批量传输SQL到数据库执行,返回每个SQL语句影响行数(数组)
void clearBatch() 清空SQL
3.单个执行SQL语句
// 方法一.Statement
Statement statement = connection.createStatement();
int row = statement.executeUpdate(sql);
ResultSet resultSet = statement.executeQuery(sql);
ResultSet默认不能反向修改数据库记录,但可通过指定参数Statement来创建可改数据的ResultSet,不建议使用,应该用update语句修改数据!!!
Statement state = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);
ResultSet resultSet = state.executeQuery("select * from user");
resultSet.next();
resultSet.updateString("name", "lioil");
resultSet.updateRow();
Statement createStatement(int resultSetType, int resultSetConcurrency)
resultSetType 结果集类型
ResultSet.TYPE_FORWARD_ONLY 不支持滚动,只能向前.
ResultSet.TYPE_SCROLL_INSENSITIVE 支持滚动,迟钝不敏感
ResultSet.TYPE_SCROLL_SENSITIVE 支持滚动,敏感
resultSetConcurrency 是否支持修改类型
ResultSet.CONCUR_READ_ONLY 不支持修改
ResultSet.CONCUR_UPDATABLE 支持修改
// 方法二.PrepareStatement预编译防止SQL注入攻击,用户参数?只做参数,不参与编译,传入SQL关键字无用
PrepareStatement statement = connection.prepareStatement("SELECT * FROM user WHERE name=? AND password=?");
statement.setString(1, "lioil");
statement.setString(2, "12345");
ResultSet resultSet = statement.executeQuery();
statement.setInt(...);
statement.setDouble(...);
statement.setDate(...);
SQL注入:用户恶意传入一些SQL特殊关键字,导致SQL语义变化
SELECT * FROM user WHERE name='lioil' AND password='12345'; -- 正常
SELECT * FROM user WHERE name='lioil' OR 1=1; --' AND password='1'; -- 恶意注入'lioil' OR 1=1; --' 使语句在OR 1=1;处结束,password没有执行
4.批量执行SQL
Statement批处理
Statement smt = conn.createStatement();
smt.addBatch(sql1);
smt.addBatch(sql2);
smt.addBatch(sql3);
smt.executeBatch();
优点:可执行多条不同结构SQL语句
缺点:没预编译,效率低
PrparedStatement批处理
PrepareStatement preSmt = conn.prepareStatement("insert into user values(null,?)");
for(int i=1;i<=100000;i++){
preSmt.setString(1,"id_"+i);
preSmt.addBatch();
// 1000条为一批次, 减少内存占用
if(i%1000==0){
preSmt.executeBatch();
preSmt.clearBatch();
}
}
preSmt.executeBatch();
优点:SQL结构相同,只编译一次,效率高
缺点:只能执行相同结构SQL语句
粘贴语句
Statement statement = connection.createStatement();
int row = statement.executeUpdate(sql);
ResultSet resultSet = statement.executeQuery(sql);
这里要把 executeUpdate
改成 executeQuery
不然会报错
String id=req.getParameter("id");
String sql="select * from user where id="+id;
String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url,"root","123456");
// System.out.println(connection);
Statement statement = connection.createStatement();
ResultSet row = statement.executeQuery(sql);
ResultSet resultSet = statement.executeQuery(sql);
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
1.移动结果集的指针/游标/当前行:
boolean next() 移到下一行, 这行有数据返回true, 无返回false
boolean Previous() 移到前一行
boolean absolute(int row) 移到指定行
boolean first() 移到首行
boolean last() 移到尾行
void beforeFirst() 移到首行前一行
void afterLast() 移到尾行后一行
2.获取数据
String getString(int cloumnCount) 根据列索引,从当前行中获得String 类型数据
String getString(String columnName) 根据列名,从当前行中获得String类型数据
String getInt(...)
String getLong(...)
String getFloat/getDouble(...)
String getDate(...)
实例:
while(resultSet.next()){
String val2 = resultSet.getString(2);
Float val3 = resultSet.getFloat(3);
Date val4 = resultSet.getDate(4);
...
}
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()) {
resp.getWriter().println(resultSet.getString("id"));
resp.getWriter().println(resultSet.getString("name"));
resp.getWriter().println(resultSet.getString("email"));
resp.getWriter().println(resultSet.getString("age"));
}
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
访问后就会有回显了
这样写的sql查询语句是不安全的
String sql="select * from user where id="+id;
比如
这里的原因就是因为它使用了 +
即会拼接我们的id,导致sql注入
这里采用 jdbc
中的预编译机制
案例
// 方法二.PrepareStatement预编译防止SQL注入攻击,用户参数?只做参数,不参与编译,传入SQL关键字无用
PrepareStatement statement = connection.prepareStatement("SELECT * FROM user WHERE name=? AND password=?");
statement.setString(1, "lioil");
statement.setString(2, "12345");
ResultSet resultSet = statement.executeQuery();
statement.setInt(...);
statement.setDouble(...);
statement.setDate(...);
SQL注入:用户恶意传入一些SQL特殊关键字,导致SQL语义变化
SELECT * FROM user WHERE name='lioil' AND password='12345'; -- 正常
SELECT * FROM user WHERE name='lioil' OR 1=1; --' AND password='1'; -- 恶意注入'lioil' OR 1=1; --' 使语句在OR 1=1;处结束,password没有执行
首先需要把sql语句改了
//原来不安全的查询语句
String sql="select * from user where id="+id;
//现在的预编译查询语句
String sql = "SELECT * FROM user WHERE id = ?";
然后设置SQL语句的参数
// 加载数据库驱动(新版驱动)
Class.forName("com.mysql.cj.jdbc.Driver");
// 连接数据库
try (Connection connection = DriverManager.getConnection(url, "root", "123456");
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
// 设置 SQL 语句的参数
preparedStatement.setString(1, id);
// 执行查询
try (ResultSet resultSet = preparedStatement.executeQuery()) {
if (!resultSet.isBeforeFirst()) {
resp.getWriter().println("未找到数据");
return;
}