一、 JDBC
1.1 使用命令行直接建立数据库和表
1. 使用命令行
打开开始菜单找到mysql-点击打开命令行-输入使用的密码:123456(机房使用root)
2. 建立数据库
-使用命令create建立数据库:“create database shop(数据库名); ”注意每一句命令后面使用分号结束-使用“use shop”打开数据库(这一句比较特殊不用加分号,不输入这句没办法打开数据库使用来创建表)
3. 创建数据表
-创建表:“create table t_user(id int auto_increment primary key,username varchar(20) not null,password varchar(20) not null,age int(3) default 0,description text);”
使用id自动增长来标识id(如果字段太长可以使用分号换行输入,只要不使用分号就会继续输入) ;char和varchar的区别在于char是定长的,varchar是变长的,char如果只有6个字节,则其余的会用空格补齐,而varchar是设置最长的字节,有多少字节就是多少字节,不会加空格补齐;auto_increment 是设置自动增长;not null不空属性,必须要有,这个属性一般是在程序实现验证不使用数据库验证,避免插入为空时不能往数据库插入导致出错没办法控制;int的默认长度是11;default 0是设置缺省值为0,没有输入时默认为0。
Ok就是提示成功了,error就要重新查看代码问题(这里直接使用键盘的上下箭头,可以直接重新调出代码,直接修改有问题的语句就可以了)
4. 查看
-剩下数据的操作就在程序里面去操作了-使用“show tables;”查看表建好没有-使用“desc t_user;”查看表的结构属性(字段,长度等); “select * from t_user; ”查看表的具体数据
5. 完整代码
- 打开mysql5.7,输入密码:“123456”
- “create database shop(数据库名); ” //建立数据库
- “use shop ”//打开使用数据库
- “create table t_user(id int auto_increment primary key,username varchar(20) not null,password varchar(20) not null,age int(3) default 0,description text);” //建表
- “show tables;”//查看表有哪些
- “desc t_user;”//查看表的结构
- “select * from t_user; ”//查看表的数据
2.2 JDBC项目
新建项目jdbc-直接点击完成-配置服务器-调试,访问http://localhost:8080/jdbc检查项目启动-打开index.jsp,修改pageEncoding为UTF-8-编写项目:
2.1. 数据库访问的步骤
2.1.1 导入数据库驱动jar包(将jar包复制到WEB-INF/lib文件夹)
导入jar有很多种方式可以创建自己的userlib,我们这里使用的jar包只适用于5.几的数据库,不适合其他8.几的数据库,我们将文件ctrl+c ctrl+v到lib下面,这里成功后会产生一个Web App Libraries,下面的jar包是无法展开的,但我们可以通过这个文件查看jar包的结构;如果没有出现上面的文件,可以右键jar包,点击Build Path,Add to Build Path来添加
2.1.2 加载驱动
使用Class.forName(className)把驱动器类加载到内存中去,就可以使用它了,className是驱动器名称,不同的数据库包括mysql不同版本的名称都比一样,“域名的倒序.产品名.类名”:
Class.forName("com.mysql.jdbc.Driver");
2.1.3 获得连接
因为我们要使用驱动,所以需要导入mysql包,所有mysql的包都是在java.sql.*下面,所以我们在import中导入或者在写一条page指令,所有数据库访问的包都在这下面(在servlet中导包方式就不一样,因为有实现类和实现接口,导的时候就要导导接口,而不是实现类,因为要注意面向接口编程)
<%@ page language="java" import="java.util.*,java.sql.*" page
Encoding="UTF-8"%>
获得数据库的连接Connection:
String url = "jdbc:mysql://localhost:3306/shop";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
使用的是访问数据库唯一的类,选择有三个参数的get方法,并在上面定义这三个属性:
连接的url:http协议+域名/IP地址(端口号)+数据库名称;不同的数据库有不同的url,修改这三个属性可以使用不同的数据库“jdbc:mysql://”就是jdbc的协议,所有的数据库访问都是这个,只是mysql有变化;mysql默认端口是3306;http协议+域名(IP地址+端口号)访问到机器,然后再加上数据库的名称:“String url = "jdbc:mysql://localhost:3306/shop";”;局域网里面直接IP地址就可以了,互联网要访问数据库需要域名,本地就使用localhost;连接的用户user,连接的密码password
2.1.4 获得句子对象(Statement)
Statement st = connection.createStatement();
2.1.5 通过句子对象执行SQL语句
三个方法: execute(sql) 几乎不用;executeQuery(sql) 用来执行select语句,只要是select就使用这个,其它全部是executeUpdate,executeQuery(sql)返回一个ResultSet对象(结果集),查询的结果数据都存放在它里面;executeUpdate(sql) 用来执行除select语句之外的SQL语句,增加、删除、修改,返回值与前面不同,返回的是一个整数,表示SQL语句影响的记录条数。
String sql = "insert into t_user(username,password,age,description) value('user1','123456',20,'test')";
int count = st.executeUpdate(sql);
if(count==1){
out.print("数据插入成功");
}else{
out.print("数据插入失败");
}
2.1.6 完整代码及效果
<body>
<%
/**
1.导入数据库驱动jar包(将jar包复制到WEB-INF/lib文件夹)
*/
//2.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获得连接Connection
String url = "jdbc:mysql://localhost:3306/shop";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//4.获得句子对象(Statement)
Statement st = connection.createStatement();
//5.通过句子对象执行SQL语句
//三个方法 execute(sql) 几乎不用
//executeQuery(sql) 用来执行select语句
//executeUpdate(sql) 用来执行除select语句之外的SQL语句,返回一个整数,表示SQL语句影响的记录条数
String sql = "insert into t_user(username,password,age,descript
ion)value('user1','123456',20,'test')";
int count = st.executeUpdate(sql);
if(count==1){
out.print("数据插入成功");
}else{
out.print("数据插入失败");
}
%>
</body>
导完了包之后一定要重启服务器,再访问http://localhost:8080/jdbc,显示“数据插入成功”,完成后还要在命令行输入“select * from t_user;”查看表格是否增加了新的数据
2.2 编写项目
复制index.jsp页面命名为list.jsp,用来显示插入的表格数据,复制后如果出现乱码,ctrl+A重新粘贴一遍-除了sql语句其余都是一样的,我们从第5步重新开始编写:
2.2.1 executeQuery(sql)查询返回ResultSet对象
使用executeQuery(sql)来查询,返回ResultSet对象(其实是接口,返回的是它的子对象,这里也不牵涉导包的问题,我们前面已经一次性导完了,后面编写servlet代码时才考虑按需引入,如果这里编写时出现红线,可能前面代码引入出错,可以重新引入)
2.2.1 ResultSet对象的方法的使用
String sql = "select * from t_user";
ResultSet rs = st.executeQuery(sql);
rs代表我们所有存储的记录都在这个对象里面,rs这个对象有next()方法,用于查询下一条记录;rs有一个指针,查询时它是指向第一条记录之前,不能直接访问,它是指向第一条之前的位置;
使用next()方法有两个作用:它将我们的指针移动到下一条,并返回一个逻辑值,表示是否还有记录,如果指针指向一条记录就返回true,如果指向最后一条,没有记录就返回false,所以可以使用这种方法引入指针,并判断还有无记录;
- 现在要把所有数据都找出来,可以使用while循环,先while(next()),一调用这个就指向第一条数据,返回true进入循环读数据;
- 然后如果我们要做登录操作,实际上还是根据用户名和密码去数据库查询,返回一个rs,只有两种情况“成功”和“失败”,成功我们判断用户名和密码都正确了,查找到一条记录,失败了就一条记录都没有,我们使用if(rs.next())判断就可以了,看他返回真or假来判断登录情况;
- 如果我们要查询一个表里面有多少条记录使用:“select count(*) from t_user;”现在我们要取count值,可以调用rs的方法,但是它指针还是放在第一条记录之前的,一定要先调用next()方法后就可以取出这个记录了,如果一条没有就返回查询为0.
rs.next();
return rs.getInt(1);
2.2.3 表格显示数据
<table>
<tr>
<th>ID</th>
<th>用户名</th>
<th>密码</th>
<th>年龄</th>
<th>描述</th>
</tr>
</table>
<style>
table{
width: 40%;
margin:100px auto;
border:1px solid #ccc;
}
</style>
这样没有表格中间的竖线,使用给th td单独加边框,
th,td{
border:1px solid #ccc;
}
使用border-collapse: collapse;添加细线表格
我们使用rs一系列的get方法把字段取出来,根据不同类型来调用不同方法,这里有两个方法,整数的表示字段的顺序,从1开始,比如要取ID可以getInt(1),根据字段的顺序来判断,使用索引可以用来查字段的数量这种只有一个字段的数据,更加方便快捷;string就是使用字段的名称,getInt(“ID”);通常使用字符名称的方法,避免顺序错误或者添加字段后顺序更改,字段名称是不区分大小写的,与getParameter()不同
2.2.4 表格显示数据完整代码
<style>
table{
width: 40%;
margin:100px auto;
border:1px solid #ccc;
border-collapse: collapse;
}
th,td{
border:1px solid #ccc;
}
</style>
</head>
<body>
<%
/**
1.导入数据库驱动jar包(将jar包复制到WEB-INF/lib文件夹)
*/
//2.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获得连接Connection
String url = "jdbc:mysql://localhost:3306/shop";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//4.获得句子对象(Statement)
Statement st = connection.createStatement();
//5.通过句子对象执行SQL语句
//三个方法 execute(sql) 几乎不用
//executeQuery(sql) 用来执行select语句,返回一个ResultSet对象
//executeUpdate(sql) 用来执行除select语句之外的SQL语句,返回一个整数,表示SQL语句影响的记录条数
String sql = "select * from t_user";
ResultSet rs = st.executeQuery(sql);
%>
<table>
<tr>
<th>ID</th>
<th>用户名</th>
<th>密码</th>
<th>年龄</th>
<th>描述</th>
</tr>
<%
while(rs.next()){
%>
<tr>
<td><%=rs.getInt("id") %></td>
<td><%=rs.getString("username") %></td>
<td><%=rs.getString("password") %></td>
<td><%=rs.getInt("age") %></td>
<td><%=rs.getString("description") %></td>
</tr>
<%} %>
</table>
</body>
</html>
2.2.5 注册页面reg.jsp
假设项目一进去到登录页面,但是这时没有用户,要先注册;我们点击注册输入信息,点击注册按钮就到UserServlet进行注册
2.2.6 登录页面login.jsp
登录和注册访问同一个servlet,只是传入了不同参数,现在访问都是使用的dopost方法,使用不同的参数判断是登录还是注册
2.2.8 Servlet
1. 新建Servlet
右键src新建servlet-包名以servlet结尾,类名首字母大写-点击下一步-修改映射路径-点击下一步-取消构造方法-点击完成
2. 将Connection定义为全局变量
因为都要访问数据库,所以将Connection其定义为全局变量,然后使用ctrl+shift+o导入包(注意一定要选择java.sql.Connection,这里不提倡使用*导入,我们用什么导什么)
3. 加载驱动
然后加载驱动:Class.forName("com.mysql.jdbc.Driver");输入完后会提示异常,处理异常有两种方法:抛给上级处理;或我们悬停鼠标有提示直接点击添加try/catch就自己添加处理
4. 获得连接
获得连接(如果复制前面的代码,记得把Connection的定义删除掉,因为前面已经有了全局定义了),ctrl+shift+o导入包,处理异常
5. 判断是登录还是注册
判断是登录还是注册:获取用户的参数(method,我们自己定义的method,不是说后面请求方式的method)
String method = request.getParameter("method");
这里编写if代码时,为了后续添加其他方法(例如修改、删除(doget),后续再添加分支就可以),就不将注册代码写到dopost里面,而是添加一个方法
if("reg".equals(method)){
reg(request,response);
}else if("login".equals(method)){
//login(request,response);
}
6. 编写Reg方法:
(1)复制修改
我们直接复制dopost方法,修改名字为reg,然后使用request.getPara
Meter()获取信息,整形的要转换成整数;然后插入到数据库,使用组合sql语句
protected void reg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
int age = Integer.parseInt(request.getParameter("age"));
String description = request.getParameter("description");
String sql = "insert into t_user(username,password,age,
description)values('"+ username +"','" + password + "'," + age +",'" + description +"')" ;
}
(2)Sql语句
获取sql语句,导包(一定要选sql的),处理异常(不要抛给上级处理,自己捕获就可以,使用try/catch)
try {
Statement st = connection.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
执行sql语句,如果是1说明注册成功,跳转登陆页面登录,可以直接使用客户端跳转;失败打印一个注册失败,先获得使用printWriter,导包,然后输出信息,并且一定要输出编码才能输出正常的,
Statement st = connection.createStatement();
int count = st.executeUpdate(sql);
if(count==1){
response.sendRedirect("login.jsp");
else{
PrintWriter out = response.getWriter();
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
out.write("注册失败");
out.flush();
out.close();
}
(3)完整代码:
先获得所有参数,然后形成sql语句,然后把数据插入到数据库里面去,然后就成功了就可以跳转登录页面,失败则显示登录失败的信息,要执行要重启服务器,成功后跳转到登录页面后,要使用“select * from t_user;”查看数据库里面是否有该信息
protected void reg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
int age = Integer.parseInt(request.getParameter("age"));
String description = request.getParameter("description");
String sql = "insert into t_user(username,password,age,description)values('"+ username +"','" +
password + "'," + age +",'" + description +"')" ;
try {
Statement st = connection.createStatement();
int count = st.executeUpdate(sql);
if(count==1){
response.sendRedirect("login.jsp");
}else{
PrintWriter out = response.getWriter();
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
out.write("注册失败");
out.flush();
out.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
7. 接口
三种接口:Statement<-PreparedStatement<-CallableStatement
Statement是最上面的接口,有两个子接口<-PreparedStatement可以执行带参数的sql语句,使用最多,比Statement多了三个方法,Statement说的有三个方法,而PreparedStatement多了三个不带sql参数的方法,创建时就需要去指定参数<-CallableStatement调用存储课程
8. 使用PreparedStatement重写reg
(1)重写reg
复制刚刚的reg方法,将reg重命名为reg1,粘贴使用PreparedStatement重写reg,删除values里面的内容,使用“?”代替,使用带参数的sql语句来写,导包选择java.sql的,创建对象时就要把sql语句传给它,后续的executeUpdate()方法就不带sql了,传参数相当于调用父类的方法了,“int count = pst.executeUpdate(); ”这里要特别注意不要传参数,不要就会使用到父类的方法,而父类的方法不能带“?”
//Statement st = connection.createStatement();
PreparedStatement pst = connection.prepareStatement(sql);
int count = pst.executeUpdate();
(2)给“?”赋值:
Prepared是准备,我们准备好后使用一系列set方法给它赋值,根据类型不同调用不同的方法,方法有两个参数,第一个是索引,从1开始,第二个是要赋的值
pst.setString(1, username);
pst.setString(2, password);
pst.setInt(3, age);
pst.setString(4, description);
(3) 完整代码:
//Statement<- PreparedStatement<- CallableStatement
protected void reg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
int age = Integer.parseInt(request.getParameter("age"));
String description = request.getParameter("description");
String sql = "insert into t_user(username,password,age,description)values(?,?,?,?)";
try {
//Statement st = connection.createStatement();
PreparedStatement pst = connection.prepareStatement(sql);
pst.setString(1, username);
pst.setString(2, password);
pst.setInt(3, age);
pst.setString(4, description);
int count = pst.executeUpdate();//特别注意,不要传参数
if(count==1){
response.sendRedirect("login.jsp");
}else{
PrintWriter out = response.getWriter();
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
out.write("注册失败");
out.flush();
out.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
9. 检查数据库用户名是否存在
我们获取到了用户,现在要检查数据库中用户是否存在,存在就不能让他注册,可以使用username去数据库查询,返回true就是存在,那找到一条记录就已经重复,不能注册;这种参数比较少的就不用使用到PreparedStatement;使用ResultSet导包要选java.sql的,然后处理异常:
//判断用户名是否存在
private boolean isExists(String username){
String sql = "select * from t_user where username='"+ username +"'";
try {
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery(sql);
if(rs.next()){
return true;
}else{
return false;
}
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
使用,获取到数据后,使用if判断是否已经存在,存在就提示然后跳转到注册页面,我们存到了request里面,跳转也只能使用服务器端跳转了:
protected void reg(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
int age = Integer.parseInt(request.getParameter("age"));
String description = request.getParameter("description");
if(isExists(username)){
request.setAttribute("msg", "用户名已存在");
request.getRequestDispatcher("reg.jsp").forward(request, response);
return;
}
我们在reg.jsp页面可以把处理信息拿过来,告诉用户为什么还是在这个页面,使用${msg},错误提示不用管,就可以显示刚刚的信息,如果为空就什么都不显示;就跟request.getAttribute(name)是一个样的,但是request.getAttribute(name)这个方法需要我们判断是否为空,为空才不显示,这个就不需要判断,直接就可以显示信息:
也可以将其它用户填的值存起来,避免让用户再填一遍,把它保存起来,然后使用value填进去,判断这个值不为空,就填进去