JSP开发模式

一、元数据(Meata data )

描述数据的数据 String sql , 描述这份sql字符串的数据叫做元数据

数据库元数据 DatabaseMetaData
参数元数据 ParameterMetaData
结果集元数据 ResultSetMetaData


二、两种JSP开发模式

  • JSP+JAVABean

    jsp里面要直接写java代码(<%%>),维护起来比较麻烦,并且jsp的页面代码也比较臃肿。
  • JSP+JAVABean+Servlet(MVC模式)

    M:model 模型层---封装根据JAVA Bean java类 EJB
    V:view  视图层---jsp用于显示
    C:controller 控制层---Servlet接收页面,找模型层处理,然后响应数据出去

DBUtils

一、改写JDBCUtil代码(一直调用的类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package util;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtil2 {

static ComboPooledDataSource dataSource =new ComboPooledDataSource();

//获取连接对象
public static Connection getConn() throws SQLException{
return dataSource.getConnection(); //已经更改为和C3P0有关的ComboPooledDataSource类
}

// 释放资源(数据库连接池最后需要释放资源)
public static void release(Connection conn , Statement st , ResultSet rs){
closeRs(rs);
closeSt(st);
closeConn(conn);
}
public static void release(Connection conn , Statement st ){
closeSt(st);
closeConn(conn);
}


private static void closeRs(ResultSet rs){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}

private static void closeSt(Statement st){
try {
if(st != null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
st = null;
}
}

private static void closeConn(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
}

二、开源DBUtils的使用步骤

  1. 导入commons-dbutils-1.4.jar文件
  2. 新建类主要是使用QueryRunner类去加载C3P0开源数据池ComboPooledDataSource类的对象,然后调用QueryRunner类的方法就可以。

增删查改的方法:

1
2
3
4
//针对增加、删除、修改
//qu.update(sql);
//针对查询
//qu.query(sql, rsh);

三、DBUtils的增删改(1行)

步骤分析

1
2
3
4
5
6
7
8
//1.使用两种数据池中其中一种的C3P0开源数据池---通过对象去获取数据池 
ComboPooledDataSource dataSource =new ComboPooledDataSource();

//2.获取连接进行sql操作---通过DBUtils专有的QueryRunner类去接开源数据池的对象
QueryRunner qu=new QueryRunner(dataSource); //只是去简化了CRUD的代码

//3.增加/删除/插入 这三种方式只需要一行的update方法 ---要更改的参数前面写?,后面写具体值
qu.update("insert into account values(null,?,?)","aa",1000);

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package dbutils;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class TestDBUtils {

@Test
public void testinsert() throws SQLException { //测试插入功能

//1.使用两种数据池中其中一种的C3P0开源数据池
ComboPooledDataSource dataSource =new ComboPooledDataSource();

//2.获取连接进行sql操作
QueryRunner qu=new QueryRunner(dataSource); //只是去简化了CRUD的代码

//3.增加
//qu.update("insert into account values(null,?,?)","aa",1000);

//3.删除
//qu.update("delte from account where id=?",5);

//3.更新
//qu.update("update account set money=? where id=?",1000000,6);
}
}

四、DBUtils查询(2行)

查询单条

1. ResultSetHandler()匿名内部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//3.查询单条  查询到的数据还是由那个result里面,然后调用下面的handle方法(手动封装)

Account account=qu.query("select * from account where id=?",new ResultSetHandler<Account>() { //使用匿名内部类

@Override
public Account handle(ResultSet rs) throws SQLException {
Account account=new Account(); //创建一个Account类对象
while(rs.next())
{
String name=rs.getString("name"); //rs获取name
int money = rs.getInt("money"); //rs获取money

account.setName(name); //对象设置name
account.setMoney(money); //对象设置money
}
return account;
}
},6);

System.out.println(account.toString()); //调用Account类的输出方法(Account类写获取name和money的方法等)

2. BeanHandler()匿名内部类:

1
2
3
4
5

/*
Account account=qu.query("select * from account where id=?",new BeanHandler<Account>(Account.class),2); ////使用第一种方式下面的BeanHandler<T>的方式匿名内部类
System.out.println(account.toString()); //调用Account类的输出方法(Account类写获取name和money的方法等)
*/

查询所有

** BeanListHandler()**

1
2
3
4
5
List<Account> list=qu.query("select * from account",new <AcBeanListHandlercount>(Account.class));  //Account.class可以自己封装---类的字节码获取类的实例
for(Account account:list)
{
System.out.println(account.toString()); //调用Account类的输出方法(Account类写获取name和money的方法等)
}

五、完整代码(3部分)

具体实现代码框架(红色√):

第一部分:要用C3P0数据池必须要配置xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

<!-- default-config 默认的配置, -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 要和不用配置文件的set方法的后面一样 -->
<property name="jdbcUrl">jdbc:mysql://localhost/bank</property>
<property name="user">root</property>
<property name="password">njdxrjgc7777777.</property>


<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>

<!-- This app is massive! -->
<named-config name="oracle"> <!-- oracle数据库用 -->
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>

<!-- intergalactoApp adopts a different approach to configuring statement caching -->
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>

<!-- he's important, but there's only one of him -->
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1</property>
<property name="initialPoolSize">1</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">5</property>
<property name="maxStatementsPerConnection">50</property>
</user-overrides>
</named-config>

</c3p0-config>

第二部分:具体实现的TestDBUtils类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package dbutils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import demain.Account;
public class TestDBUtils {
@Test
public void testinsert() throws SQLException { //测试插入功能
//1.使用两种数据池中其中一种的C3P0开源数据池
ComboPooledDataSource dataSource =new ComboPooledDataSource(); //一定要记得导入c3p0-config.xml文件(配置文件)

//2.获取连接进行sql操作
QueryRunner qu=new QueryRunner(dataSource); //只是去简化了CRUD的代码

//3.增加
//qu.update("insert into account values(null,?,?)","aa",1000);

//3.删除
//qu.update("delte from account where id=?",5);

//3.更新
//qu.update("update account set money=? where id=?",1000000,6);

//3.查询单条 查询到的数据还是由那个result里面,然后调用下面的handle方法(手动封装)
/*
Account account=qu.query("select * from account where id=?",new ResultSetHandler<Account>() { //使用匿名内部类

@Override
public Account handle(ResultSet rs) throws SQLException {
Account account=new Account(); //创建一个Account类对象
while(rs.next())
{
String name=rs.getString("name"); //rs获取name
int money = rs.getInt("money"); //rs获取money

account.setName(name); //对象设置name
account.setMoney(money); //对象设置money
}
return account;
}
},6);
System.out.println(account.toString());
*/

//3.查询单条
/*
Account account=qu.query("select * from account where id=?",new BeanHandler<Account>(Account.class),2); ////使用第一种方式下面的BeanHandler<T>的方式匿名内部类
System.out.println(account.toString());
*/

//3.查询所有的信息
List<Account> list=qu.query("select * from account",new BeanListHandler<Account>(Account.class)); //Account.class可以自己封装---类的字节码获取类的实例
for(Account account:list)
{
System.out.println(account.toString());
}

}
}

第三部分:要生成和存取的Account类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package demain;

public class Account {

private String name; //写数据库的两个属性
private int money;

//下面是两个属性的set和get方法

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}

//输出的tostring方法

@Override
public String toString() {
return name+" "+money;
}

}

结果展示:


数据库连接池

一、数据库连接池

1.数据库的连接对象创建工作,比较消耗性能。

2.一开始现在内存中开辟一块空间(集合) , 一开先往池子里面放置 多个连接对象。 后面需要连接的话,直接从池子里面去。不要去自己创建连接了。 使用完毕,要记得归还连接。确保连接对象能循环利用。


二、自定义数据库连接池

步骤分析:

使用单元Junit测试

  1. 首先导入之前写好的JDBCUtil类和外部文件jdbc.properties还有jar文件。

  2. 新建一个MyDataSource类:
    需要继承DataSource类–实现所有方法(我们只需要完成getConnection()方法)–只需要管连接池对外公布的获取连接的方法getConnection()。
    2.1 建立MyDataSource方法:先往池子里面放10个连接(建立list集合存放),使用JDBCUtil.getConn()去获取连接,通过循环和add方法连续添加10个连接。
    2.2 在连接池对外公布的获取连接的方法中去通过remove移除(取连接)
    2.3 最后写一个方法addBack():用完之后归还。

  3. 如果现在链接不够的话,就要在取之前判断是否为0,如果不够就要for循环继续add()。

  4. 在项目处右键–构建路径–右边选Add Library–添加Junit4选项

  5. 新建测试的TestPool类:
    5.1 需要建立MyDataSource对象然后调用getConnection()连接
    5.2 写sql语句 –MyDataSource对象调用prepareStatement去检测SQL语句
    5.3 检测sql语句之后调用对应的executeUpdate()语句
    5.4 finally里面要写关于sql操作之后关闭对象–还需要通过对象调用addBack()方法

具体代码:

JDBCUtil类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package util;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtil {

static String driverClass = null;
static String url = null;
static String name = null;
static String password= null;

static{
try {
//1. 创建一个属性配置对象
Properties properties = new Properties();
InputStream is = new FileInputStream("jdbc.properties");

//使用类加载器,去读取src底下的资源文件。 后面在servlet
// InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
//导入输入流。
properties.load(is);

//读取属性
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
name = properties.getProperty("name");
password = properties.getProperty("password");

} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取连接对象
* @return
*/
public static Connection getConn(){
Connection conn = null;
try {
Class.forName(driverClass);
//静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver());
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb");
//2. 建立连接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。
conn = DriverManager.getConnection(url, name, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}

/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn , Statement st , ResultSet rs){
closeRs(rs);
closeSt(st);
closeConn(conn);
}
public static void release(Connection conn , Statement st ){
closeSt(st);
closeConn(conn);
}


private static void closeRs(ResultSet rs){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}

private static void closeSt(Statement st){
try {
if(st != null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
st = null;
}
}

private static void closeConn(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
}

jdbc.properties代码(只需要更改数据库的名字)

1
2
3
4
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/bank
name=root
password=njdxrjgc7777777.

MyDataSource(实现DataSource类)代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package util;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
/*
* 这是一个数据库连接池
* 一开始先往池子放10个连接
*
* 1.开始创建10个连接
*
* 2.取出去
*
* 3.拿回来
*
* 4.扩容
*
*/
public class MyDataSource implements DataSource{ //继承之后需要实现下面的方法(点击弹出)

List <Connection> list=new ArrayList<Connection>(); //建立list集合存放连接

public MyDataSource() { //添加10个连接
for(int i=0;i<10;i++)
{
Connection conn=JDBCUtil.getConn(); //提前拷贝JDBCUtil类和jdbc.properties外部文件和外部jar文件
list.add(conn); //add往里添加
}

}

//连接池对外公布的获取连接的方法
@Override
public Connection getConnection() throws SQLException {
//来拿连接的时候,先看看池子里面是否还有
if(list.size()==0) //如果没有了继续添加5个
{
for(int i=0;i<5;i++)
{
Connection conn=JDBCUtil.getConn(); //提前拷贝JDBCUtil类和jdbc.properties外部文件和外部jar文件
list.add(conn); //扩容
}
}

Connection conn = list.remove(0); //移除第一个(自动往前补) 相当于取连接
return conn;
}

public void addBack(Connection conn) { //自己写的在用完之后要归还的方法
list.add(conn); //add在加回去
}

//-------只需要看上面的连接方法----------

@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO 自动生成的方法存根
return null;
}

@Override
public int getLoginTimeout() throws SQLException {
// TODO 自动生成的方法存根
return 0;
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO 自动生成的方法存根
return null;
}

@Override
public void setLogWriter(PrintWriter arg0) throws SQLException {
// TODO 自动生成的方法存根

}

@Override
public void setLoginTimeout(int arg0) throws SQLException {
// TODO 自动生成的方法存根

}

@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO 自动生成的方法存根
return false;
}

@Override
public <T> T unwrap(Class<T> arg0) throws SQLException {
// TODO 自动生成的方法存根
return null;
}


@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO 自动生成的方法存根
return null;
}

}

测试JUnit的TestPool代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class TestPool {
@Test
public void testPool(){
Connection conn=null;
PreparedStatement ps =null;
MyDataSource my=new MyDataSource(); //调用我们写好的MyDataPool类

try {
conn = my.getConnection(); //第一步获取连接
String sql="insert into account values(4,'xilali',10)"; //第二步写sql语句
ps = conn.prepareStatement(sql); //第三步调用方法检测sql语句
ps.executeUpdate(); //调用方法对sql执行

} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
try {
ps.close(); //关闭对象 替换了之前的JDBCUtil.release(conn,ps,rs);
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//归还连接
my.addBack(conn); //用完之后一定要记得addBack归还连接
}
}
}

单元测试只是显示绿色/红色不通过—具体结果去看看数据库:


三、自定义数据库连接池存在的问题

  • 因为sun公司针对数据库连接池的定义没有用完返回的解决方案—->需要额外记住写addBack()方法

  • 单例

  • 不能面向接口编程

1
2
3
4
5
6
7
8
9
//面向对象编程
MyDataSource my=new MyDataSource(); //调用我们写好的MyDataSource类

//现在面向接口编程
DataSource dataSource = new MyDataSource(); //改成这样因为接口里面没有定义addBack方法
dataSource.addBack(); //会报错

//而且原来sun公司的Connection对于close()方法直接是关闭 ---我们需要直接改成返回连接
conn.colse();

四、解决存在的问题

  1. 修改接口中的那个close方法

  2. 原来的Connection对象的close方法,是真的关闭连接。

  3. 打算修改这个close方法,以后在调用close,并不是真的关闭,而是归还连接对象。


五、扩展方法

原有的方法逻辑,不是我们想要的。 想修改自己的逻辑

  1. 直接改源码—-无法实现。

  2. 继承—-必须得知道这个接口具体实现是谁。

  3. 使用装饰者模式(扩展一个类去更改方法)

  先是一个接口Waiter里面有service()方法,最后有一个Waitress类实现刚才的接口输出方法内容,但是现在可以通过建立WaitressWrap()方法去用构造方法获取接口对象,this获取对象,然后通过实现接口方法更改以前的操作。


六、装饰者模式–具体操作

我们新建ConnectionWrap类实现Connection接口的方法,但是只需要去更改close()和prepareStatement()方法。

1.只展示实现类ConnectionWrap类部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ConnectionWrap implements Connection{   //继承Connnection方法 修改它的close更改为 返回连接

Connection connection=null; //创建连接
List<java.sql.Connection> list;

public ConnectionWrap(java.sql.Connection conn, List<java.sql.Connection> list) { //构造方法
super();
this.connection =connection;
this.list=list;
}

@Override
public void close() throws SQLException { //更改了close()方法 ---变成了归还连接
list.add(connection);
}

@Override
public PreparedStatement prepareStatement(String sql) throws SQLException { //更改了里面的检验sql语句
return connection.prepareStatement(sql);
}

2.在MyDataSource类里面在取连接之前对connection对象进行调用COnnectionWrap类包装,

1
2
3
//抛出前,对对象进行包装
Connection connection=new ConnectionWrap(conn,list);
return connection;

3.在测试类之中以后就不用写归还的方法,而是直接写JDBCUtil的方法:

1
2
//归还连接
JDBCUtil.release(conn,ps);

七、开源连接池(DBCP/C3PO)

DBCP使用步骤:

###1. 导入对应的dbcp和pool的jar文件:

###2.1 不使用配置文件( BasicDataSource类):

思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.通过BasicDataSource类构件数据源对象
BasicDataSource dataSource = new BasicDataSource();

2.不使用配置文件---使用set方法添加那四行材料
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/bank"); //jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库
dataSource.setUsername("root");
dataSource.setPassword("njdxrjgc7777777.");

3.通过对象调用连接
conn = dataSource.getConnection(); //使用dataSource调用连接

4.书写SQL语句,然后去检验,添加,方法上传
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "admin"); //给下标1和2的设定具体的值
ps.setInt(2, 1000);

ps.executeUpdate(); //调用插入操作方法

5.finally最后使用JDBCUtil类的方法抓异常
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package kaiyuan;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
import org.junit.Test;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
import util.JDBCUtil;
public class DBCPDemo {
@Test
public void testDBCP01(){
java.sql.Connection conn = null;
java.sql.PreparedStatement ps = null;
try {
//1. BasicDataSource类构建数据源对象
BasicDataSource dataSource = new BasicDataSource();

//连的是什么类型的数据库, 访问的是哪个数据库 , 用户名, 密码。。
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/bank"); //jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库
dataSource.setUsername("root");
dataSource.setPassword("njdxrjgc7777777.");

//2. 得到连接对象
conn = dataSource.getConnection(); //使用dataSource调用连接
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "admin"); //给下标1和2的设定具体的值
ps.setInt(2, 1000);

ps.executeUpdate(); //调用插入操作方法

} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
}
}
}

###2.2 使用配置文件( BasicDataSourceFactory类):

思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.通过BasicDataSourceFactory类构件数据源对象
BasicDataSourceDactory dataSource = new BasicDataSourceDactory();

2.使用配置文件-
//构造配置文件对象
Properties properties=new Properties();

//输入流加载文件(找到配置文件)
InputStream is=new FileInputStream("src//dbcpconfig.properties");
properties.load(is); //对象调用load加载文件(加载好)

DataSource dataSource = factory.createDataSource(properties); //输入流加载文件之后要用对象调用方法加载配置文件(加载到对象去)


3.通过对象调用连接(sun公司的DataSource)
conn = dataSource.getConnection(); //使用dataSource调用连接

4.书写SQL语句,然后去检验,添加,方法上传
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "liangchaowei"); //给下标1和2的设定具体的值
ps.setInt(2, 1020);

ps.executeUpdate(); //调用插入操作方法

5.finally最后使用JDBCUtil类的方法抓异常
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package kaiyuan;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import util.JDBCUtil;
public class DBCPDemo02 {

@Test
public void testDBCP02() throws Exception{

//和之前的代码一样
java.sql.Connection conn = null;
java.sql.PreparedStatement ps = null;
try {
//1. BasicDataSourceFactory类构建数据源对象(因为有可以加载配置文件的方法)
BasicDataSourceFactory factory=new BasicDataSourceFactory();

//构造配置文件对象
Properties properties=new Properties();
//输入流加载文件
InputStream is=new FileInputStream("src//dbcpconfig.properties");
properties.load(is); //输入流加载配置文件

DataSource dataSource = factory.createDataSource(properties); //输入流加载文件之后要用对象调用方法加载配置文件

//2. 得到连接对象
conn = dataSource.getConnection(); //使用dataSource调用连接
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "liangchaowei"); //给下标1和2的设定具体的值
ps.setInt(2, 1020);

ps.executeUpdate(); //调用插入操作方法

} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
}

}
}

dbcpconfig.properties配置文件(放在src下面设置好连接池的连接初始化/最大数等等):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/bank
username=root
password=njdxrjgc7777777.

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000


#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

J3P0使用步骤:

###1. 导入对应的c3p0-0.9.1.2.jar文件:

###2.1 不使用配置文件方式(ComboPooledDataSource类)

思路:

1
和DBCP数据池的不实用配置文件方式一样,只不过一个是BasicDataSource类、另外一个是ComboPooledDataSource类

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Connection conn = null;
PreparedStatement ps = null;
try {
//1. 创建datasource
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2. 设置连接数据的信息

dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");
dataSource.setUser("root");
dataSource.setPassword("root");

//2. 得到连接对象
conn = dataSource.getConnection();
String sql = "insert into account values(null , ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "admi234n");
ps.setInt(2, 103200);

ps.executeUpdate();

} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}

###2.2 使用配置文件方式(ComboPooledDataSource)

思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.需要导入c3p0-config.xml解析文件(名字不能更改!!!!)

2.跟以前的相比只需要第一步new一个ComboPooledDataSource对象
ComboPooledDataSource dataSource=new ComboPooledDataSource();

3.然后通过对象getConnection()获得连接。
conn = dataSource.getConnection();

4.新建sql语句,通过连接检验sql语句,添加下标内容,调用插入语句
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "adminsan"); //给下标1和2的设定具体的值
ps.setInt(2, 1050);

ps.executeUpdate(); //调用插入操作方法

5.finally最后调用JDBCUtil类方法
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法

c3p0-config.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

<!-- default-config 默认的配置, -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 要和不用配置文件的set方法的后面一样 -->
<property name="jdbcUrl">jdbc:mysql://localhost/bank</property>
<property name="user">root</property>
<property name="password">njdxrjgc7777777.</property>


<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property> <!-- 关于连接池的细节设计 -->
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>

<!-- This app is massive! -->
<named-config name="oracle"> <!-- oracle数据库用 -->
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>

<!-- intergalactoApp adopts a different approach to configuring statement caching -->
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>

<!-- he's important, but there's only one of him -->
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1</property>
<property name="initialPoolSize">1</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">5</property>
<property name="maxStatementsPerConnection">50</property>
</user-overrides>
</named-config>

</c3p0-config>

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package kaiyuaner;
import java.sql.SQLException;
import javax.sql.CommonDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import util.JDBCUtil;
public class C3P0Demo {

@Test
public void C3P0Demo01(){
java.sql.Connection conn = null;
java.sql.PreparedStatement ps = null;
try {

//1.只需要new一个对象(找路径什么的C3P0帮你完成)
ComboPooledDataSource dataSource=new ComboPooledDataSource(); //xml里面有两种数据库(默认会找default-config分支) 如果需要用oracle数据库就("oracle")

//2. 得到连接对象
conn = dataSource.getConnection(); //使用dataSource调用连接
String sql = "insert into account values(null , ? , ?)"; //sql插入语句
ps = conn.prepareStatement(sql); //检验sql语句

ps.setString(1, "adminsan"); //给下标1和2的设定具体的值
ps.setInt(2, 1050);

ps.executeUpdate(); //调用插入操作方法

} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
}

}
}

事务

一、事务(Transaction)

Transaction 其实指的一组操作,里面包含许多个单一的逻辑。只要有一个逻辑没有执行成功,那么都算失败。 所有的数据都回归到最初的状态(回滚)。

使用命令行方式演示事务

  • 开启事务

    start transaction;

  • 提交/回滚事务

    commit—-提交事务(数据将会写到磁盘上的数据库)
    rollback—-数据回滚,回到最初的状态。

  1. 关闭自动提交功能。

  1. 演示事务

使用代码方式演示事务(针对连接)

1
2
3
4
5
1. 关闭自动提交的设置  conn.setAutoCommit(false )

2. 提交事务 conn.commit();

3. 回滚事务 conn.rollback();

单元测试代码(只是在连接前关闭自动提交,然后操作结束后关闭事务,然后错误的时候提示回滚)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Test  //使用单元Junit测试
public void testTransaction(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
conn = JDBCUtil.getConn();

//连接,事务默认就是自动提交的。 关闭自动提交。
conn.setAutoCommit(false);

String sql = "update account set money = money - ? where id = ?";
ps = conn.prepareStatement(sql);

//扣钱, 扣ID为1 的100块钱
ps.setInt(1, 100);
ps.setInt(2, 1);
ps.executeUpdate();

int a = 10 /0 ;
//加钱, 给ID为2 加100块钱
ps.setInt(1, -100);
ps.setInt(2, 2);
ps.executeUpdate();

//成功: 提交事务。
conn.commit();

} catch (SQLException e) {
try {
//事变: 回滚事务
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();

}finally {
JDBCUtil.release(conn, ps, rs);
}
}

二、事务知识点

事务特性(ACID)

  • 原子性

指的是 事务中包含的逻辑,不可分割。

  • 一致性

指的是 事务执行前后,数据完整性.

  • 隔离性

指的是 事务在执行期间不应该受到其他事务的影响

  • 持久性

指的是 事务执行成功,那么数据应该持久保存到磁盘上。

事务的安全隐患

不考虑(×)隔离级别设置,那么会出现以下问题:

脏读:一个事务读到另外一个事务还未提交的数据
不可重读: 一个事务读到了另外一个事务提交的数据,造成了前后两次查询结果不一致。
幻读

丢失更新:两个事物对同一个事务进行更改,A事务更改之后B更改/B回滚,A更改的被覆盖。

解决方法:

  1. 悲观锁

    可以在查询的时候,加入 for update(A更改之后让B停止更改,然后B之后可以改)

  1. 乐观锁

    要求程序员自己控制(添加version版本)

可串行化(顺序)

  如果有一个连接的隔离级别设置为了串行化 ,那么谁先打开了事务, 谁就有了先执行的权利, 谁后打开事务,谁就只能得着,等前面的那个事务,提交或者回滚后,才能执行。
  但是这种隔离级别一般比较少用。 容易造成性能上的问题。 效率比较低。

  • 效率划分,从高到低

读未提交 > 读已提交 > 可重复读 > 可串行化

  • 拦截程度 ,从高到底

可串行化 > 可重复读 > 读已提交 > 读未提交

两种数据库默认隔离级别:

mySql 默认的隔离级别是 可重复读

Oracle 默认的隔离级别是 读已提交


学生管理系统

一、分析具体功能

概述图

具体分析

  第一步:我们可以通过分析得到,我们需要建立数据库stus(用户表t_user和学生表t_stu),通过Navicat配合mysql5.0建立表。

  • 此为学生表

  • 此为用户表

  第二步:我们需要建立两个jsp文件:一个需要写注册的login.jsp页面,另外一个是通过判断之后弹出得到的学生信息stu_list.jsp页面。
而对于登录的界面需要去写三个input标签分别是账号密码和提交。然后学生信息界面我们这次做的是展示所有信息,其中就需要有一个form表单,然后表单里面对于展示所有信息就需要使用EL表达式配合JSTL的内置标签c:forEach去遍历结果,但是需要先导包。

  第三步:我们需要去做一个前两步之间的login_servlet类,通过servlet需要获取信息(通过request.getParameter()方法),查询数据库(利用之前学过的jdbc用过的javaee结构),之后判断信息是否与数据库匹配(利用方法去查),最后给出判断(跳转还是给出提示)。
  
  第四步:我们去完成jdbc关于查询数据库的操作。需要对于用户建立UserDao(写数据库操作的方法)/UserDaoImpl(通过获取后面这个类的属性,然后达到查询的功能)/JDBCUtil类(套之前写过的模板主要去创建属性配置对象,利用类加载器获取外部文件jdbc.properties,然后加载读取的信息)

  第五步:去完成关于学生的操作,需要stuDao(和用户一样去写操作的代码)/Student(写每个学生信息项的get和set方法)/StuDaoImpl(和用户类一样,主要是查询)


二、具体代码:

StuDao类

1
2
3
4
5
6
7
package dao;
import java.util.List;
import domain.Student;
public interface StuDao {
//查询出来所有的学习信息
List<Student> findAll();
}

UserDao类

1
2
3
4
5
package dao;
//该Dao定义了对于用户表访问的规则
public interface UserDao {
boolean login(String username,String password);
}

Student类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package domain;
public class Student {
//到底有多少成员,想要在页面上显示多少
private int id;
private String name;
private int age;
private String gender;
private String address;

//鼠标右键-源码-生成get和set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

StuDaoImpl类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import dao.StuDao;
import domain.Student;
import util.JDBCUtil;
public class StuDaoImpl implements StuDao {
@Override
public List<Student> findAll() {
List<Student> list=new ArrayList();
Connection conn = null;
PreparedStatement ps =null;
ResultSet rs=null;
try {
//1.得到连接对象
conn = JDBCUtil.getConn();

String sql="select * from t_stu";

ps = conn.prepareStatement(sql);

rs = ps.executeQuery();

//数据多了用对象装,对象多了用集合装
while(rs.next()) { //循环

Student stu=new Student(); //创建学生
//调用Student类里面的set get方法
stu.setId(rs.getInt("id"));
stu.setAge(rs.getInt("age"));
stu.setName(rs.getString("name"));
stu.setGender(rs.getString("gender"));
stu.setAddress(rs.getString("address"));

list.add(stu);
}

} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally{
JDBCUtil.release(conn,ps,rs);
}
return list; //需要返回装学生信息的集合
}
}

UserDaoImpl类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import dao.UserDao;
import util.JDBCUtil;
public class UserDaoImpl implements UserDao {
@Override
public boolean login(String username,String password)
//用finally所以将三个都提出来
Connection conn = null;
PreparedStatement ps =null;
ResultSet rs=null;
try {
//1.得到连接对象
conn = JDBCUtil.getConn();

//预先对sql语句执行语法的校验, ? 对应的内容,后面不管传递什么进来,都把它看成是字符串。 or select
String sql="select * from t_user where username=? and password=?";

//2.创建ps对象
ps = conn.prepareStatement(sql);
//? 对应的索引从 1 开始。
ps.setString(1, username);
ps.setString(2, password);

//3.开始执行
rs = ps.executeQuery(); //调用查询语句

//如果能成功移到下一条记录,那么表明有这个用户
return rs.next();
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally{
JDBCUtil.release(conn,ps,rs);
}
return false;
}
}

loginServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package Servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import dao.StuDao;
import dao.UserDao;
import domain.Student;
import impl.StuDaoImpl;
import impl.UserDaoImpl;
/**
*用于处理登录的servlet
*/
public class loginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//提交的数据可能有中文: 1.在tomcat里面永久改 2.request方式
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");

//1.获取客户端login里面的账号和密码
String username = request.getParameter("username");
String password = request.getParameter("password");

//左父右子(面向接口)创建对象

//2.去数据库访问有没有这个用户
UserDao dao=new UserDaoImpl();
boolean isSuccess = dao.login(username, password);

//3.针对Dao的结果做出响应
if(isSuccess)
{
//1.查询出来所有的学生信息
StuDao stuDao=new StuDaoImpl();
List<Student> list = stuDao.findAll();

//2.先把这个集合存到作用域
request.getSession().setAttribute("list", list);


//2.response的重定向 --跳转到学生的jsp
response.sendRedirect("stu_list.jsp");

}
else
{
response.getWriter().write("登录失败!请重新输入用户名和密码!");
}

}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

}

JDBCUtil类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package util;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtil {
static String driverClass = null;
static String url = null;
static String name = null;
static String password= null;
static{
try {
//1. 创建一个属性配置对象
Properties properties = new Properties();
// InputStream is = new FileInputStream("jdbc.properties");


//使用类加载器,去读取src底下的资源文件。 后面在servlet
InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
//导入输入流。
properties.load(is);

//读取属性
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
name = properties.getProperty("name");
password = properties.getProperty("password");

} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取连接对象
*/
public static Connection getConn(){
Connection conn = null;
try {
Class.forName(driverClass);
//静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver());
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb");
//2. 建立连接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。
conn = DriverManager.getConnection(url, name, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}

/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn , Statement st , ResultSet rs){
closeRs(rs);
closeSt(st);
closeConn(conn);
}
public static void release(Connection conn , Statement st){
closeSt(st);
closeConn(conn);
}


private static void closeRs(ResultSet rs){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}

private static void closeSt(Statement st){
try {
if(st != null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
st = null;
}
}

private static void closeConn(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
}

需要导入的外部文件jdbc.properties

1
2
3
4
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/stus
name=root
password=njdxrjgc7777777.

stu_list.jsp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<!-- 导入jsp的JSTL包 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>学生信息管理系统</title>
</head>
<body>
<br>学生列表:</br>
<table bordent="1" width="700px">

<tr align="center">
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
<td>住址</td>
<td>操作</td>
</tr>

<!-- JSTL里面的标签-->
<c:forEach var="stu" items="${list}">
<tr align="center">
<td>${stu.id}</td>
<td>${stu.name}</td>
<td>${stu.age}</td>
<td>${stu.gender}</td>
<td>${stu.address}</td>
<td><a href="#">更新</a> <a href="#">删除</a></td>
</tr>
</c:forEach>

</table>

</body>
</html>

login.jsp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>欢迎使用黑马学生管理系统</h2>
<form action="loginServlet" method="post">
账号:<input type="text" name="username"></br>
密码:<input type="text" name="password"></br>
<input type="submit" value="登录">
</form>
</body>
</html>

登录界面:

成功后弹出界面:

JSTL

一、JSTL(JSP Standard Tag Library )

  简化jsp的代码编写,替换JSP中<% %>写法.与EL表达式(${})配合

二、使用步骤

  1. 导入jar文件到工程的WebContent/Web-Inf/lib
    (文件在我的f盘的jar里)

  1. 在jsp页面上,使用taglib指令,来引入标签库
1
2
3
4
5
6
	<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
```
**注意**: 如果想支持 EL表达式,那么引入的标签库必须选择1.1的版本,1.0的版本不支持EL表达式。

---
# 二、常用标签(set/if/forEach) #
<c:set></c:set>
<c:if test=""></c:if>
<c:forEach></c:forEach>
1
## c:set ##
<!-- 声明一个对象name, 对象的值 zhangsan , 存储到了page(默认) , 指定是session -->
    <c:set var="name" value="zhangsan" scope="session"></c:set>

<!-- 使用EL标签的内置对象sessionScope取出 -->
    ${sessionScope.name }
1
2

## c:if ##
<!-- 声明一个对象age,对象的值 18 ,存储到了page(默认)-->
<c:set var="age" value="18" ></c:set>

    <c:if test="${ age > 26 }">
        年龄大于了26岁...
    </c:if>

    <c:if test="${ age <= 26 }">
        年龄小于了26岁...
    </c:if>

定义一个变量名 flag  去接收前面表达式的值,然后存在session域中

<c:if test="${ age > 26 }" var="flag" scope="session">
    年龄大于了26岁...
</c:if>
1
2

## c:forEach ##
从1 开始遍历到10 ,得到的结果 ,赋值给 i ,并且会存储到page域中, step , 增幅为2, 
    <c:forEach begin="1" end="10" var="i" step="2">
        ${i}
    </c:forEach>

<c:forEach var="user" items="${list}">
    ${user.name } ----${user.age }
</c:forEach>

```

EL表达式

一、概述#

是为了简化jsp代码,具体一点就是为了简化在jsp里面写的那些java代码。

二、写法格式

1
${表达式}

如果从作用域中取值,会先小的作用域开始取,如果没有,就往下一个作用域取。 一直把四个作用域取完都没有, 就没有显示。

三、简单使用

简单取数:##

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<% 
pageContext.setAttribute("name","pageContext");
request.setAttribute("name","request");
session.setAttribute("name","session");
application.setAttribute("name","application");
%>

以普通方式取出数据:</br>
<%=pageContext.getAttribute("name")%>
<%=request.getAttribute("name")%>
<%=session.getAttribute("name")%>
<%=application.getAttribute("name")%>
</br>

以EL表达式取出数据:</br>
${pageScope.name} //直接精确的去找name
${requestScope.name}
${sessionScope.name}
${applicationScope.name}
</br>

细节:
   ${ name }:找寻顺序是:page –> request –> session –>application
   ${ sessionScope.name } :精确地寻找(Scope) 到session的

数组

1
2
3
4
5
6
7
8
9
10
11
12
<% 
String[] a={"aa","bb","cc","dd"}; //注册一个a数组
pageContext.setAttribute("array",a); //将a数组的存到array数组
%>
</br>

数组中的值:</br>
${array[0]} //然后利用下标找(有下标通过 [] )
${array[1]}
${array[2]}
${array[3]}
</br>

List集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<% 
List list=new ArrayList(); // alt+/ 提示出然后加import

list.add("11"); //list集合添加元素是add()方法
list.add("22");
list.add("33");
list.add("44");
pageContext.setAttribute("array",list); //将list数组的存到array数组
%>

List集合:</br>
${array[0]} //然后利用[]找(有下标通过 [] )
${array[1]}
${array[2]}
${array[3]}
</br>

Map集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<% 
Map map=new HashMap(); // alt+/ 提示出然后加import

map.put("name","zhangsan"); //map集合添加元素是put()方法
map.put("age","19");
map.put("address","南京...");

pageContext.setAttribute("array",map); //将map数组的存到array数组
%>

Map集合:</br>
${array.name} //然后利用.找(没有下标通过 . )
${array.age}
${array.address}
</br>

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.如果这份值是有下标的,那么直接使用[]

<%
String [] array = {"aa","bb","cc"}
session.setAttribute("array",array);
%>

${ array[1] } --> 这里array说的是attribute的name


2.如果没有下标,直接使用 .的方式去取

<%
User user = new User("zhangsan",18);
session.setAttribute("u", user);
%>

${ u.name }
${ u.age }

四、EL表达式的11个内置对象

  相对于JSP的9个内置对象而言,EL加强了其中的几个方面得到了11个内置对象。

使用模板:

1
${ 对象名.成员 }
  • pageContext

作用域相关对象(JSP对应的作用域pagecontext/request/session/application)

  • pageScope
  • requestScope
  • sessionScope
  • applicationScope

头信息相关对象

  • header
  • headerValues

参数信息相关对象

  • param
  • paramValues
  • cookie
    全局初始化参数
  • initParam

JSP

一、JSP(JAVA Sever page)

从用户看待–网页
从程序猿看待–java类继承了servlet

jsp和html的区别:

html:大多数显示静态内容。
jsp:里面可以写java代码,动态改变东西可以展示在页面内。

我们在web文件内新建JSP文件(初始):

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

二、 page指令(第一行)

分析第一行的<%%>代码

1
2
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
  1. language:表明jsp页面用什么书写(java)
    1
    language="java"
  2. contentType:表明什么类型的文件,用什么编码(response里面文字编码就用的这个)
    1
    contentType="text/html; charset=UTF-8"
  3. pageEncoding:jsp内容编码(body里面的内容)
    1
    pageEncoding="UTF-8"
  4. extends:用于指定jsp翻译成java文件后,继承的父类是谁,一般不用改。
  5. import:导包使用的,一般不用手写。
    1
    <%@ import.java.util %>
  6. *session *:
    1
    session="true/false"

    用于控制在这个jsp页面里面,能够直接使用session对象。
    如果是true:那么会有getSession()的调用,
    如果是false:那么就不会有该方法调用,也就是没有session对象了。在页面上自然也就不能使用session了。

  7. errorPage:指的是错误的页面,值需要给错误的页面路径
  8. isErrorPage:上面的errorPage 用于指定错误的时候跑到哪一个页面去。 那么这个isErroPage , 就是声明某一个页面到底是不是错误的页面。

三、include指令(包含其他的jsp内容)

包含另外一个jsp的内容(也包括标签元素的内容)进来。

1
<%@ include file="other02.jsp" %>

四、taglib指令

1
<%@ taglib prefix=""  uri=""%>

perfix: 标签库路径
uri: 标签库路径


五、JSP动作标签(只介绍三个)

1
2
3
4
5
<jsp:include page=""></jsp:include>

<jsp:param value="" name=""/>

<jsp:forward page=""></jsp:forward>

jsp:include(包含)

包含指定的页面,这里是动态包含而是把它的运行结果拿过来。

1
2
3
4
5
//include动作标签(运行结果):
<jsp:include page="other02.jsp"></jsp:include>

//include指令(标签元素):
<%@ include file="other02.jsp" %>

jsp:forward(跳转)##

1
2
3
4
5
6
//jsp动作标签
<jsp:forward page="other02.jsp"></jsp:forward>
//可以用请求转发(request)替换
<%
request.getRequestDispatcher("other02.jsp").forward(request, response);
%>

jsp:param(添加参数)

意思是: 在包含某个页面的时候,或者在跳转某个页面的时候,加入这个参数。

1
2
3
4
5
6
7
	
<jsp:forward page="other02.jsp">
<jsp:param value="beijing" name="address"/>
</jsp:forward>

//在other02.jsp中获取输出参数
<%= request.getParameter("address")%>

六、JSP内置对象(JSP页面直接使用)

九个JSP内置对象整体概述

第一部分:四个作用域

  • pageContext 【PageContext】
  • request    【HttpServletRequest】
  • *session *    【HttpSession】
  • application  【ServletContext】

第二部分:两个对比

  • out     【JspWriter】
  • response   【HttpServletResponse】
    如果同时拥有response和out的输出,那么会把out整合到response里面。先输出response的,后是out的

第三部分:不常用
-** exception** 【Throwable】

  • page    【Object】 就是这个jsp翻译成的java类的实例对象
  • config   【ServletConfig】

四个作用域:

pageContext 【PageContext】
  request   【HttpServletRequest】
  session   【HttpSession】
application 【ServletContext】

这些对象可以存值(setAttribute),但是他们的取值范围(getAttribute)有限定.

1
2
3
4
5
6
7
8
9
10
11
12
13
存储<br>
<%
pageContext.setAttribute("name", "page");
request.setAttribute("name", "request");
session.setAttribute("name", "session");
application.setAttribute("name", "application");
%>

取出<br>
<%=pageContext.getAttribute("name")%>
<%=request.getAttribute("name")%>
<%=session.getAttribute("name")%>
<%=application.getAttribute("name")%>

四个作用域的大小

  • pageContext 【PageContext】

仅限于当前的页面

还可以获取到其他八个内置对象。

  • request 【HttpServletRequest】

仅限于一次请求,只要服务器对该请求做出了响应。 这个域中存的值就没有了。

  • session 【HttpSession】

限于一次会话(多次请求与响应)当中。

  • application 【ServletContext】

整个工程都可以访问,服务器关闭后就不能访问了。

Session实现购物车

一、购物车实现分析

  首先分为四个部分:分别是jsp的商品列表、CartServlet判断类,然后可以跳转商品列表的jsp界面和Cart.jsp界面(购物车结算)

CartServlet判断类:

1.先通过request.getParameter("id")获得jsp代码里面href里面的id号;然后通过names数组存放对应的手机名字
2.通过map集合存放对应的 手机号 数量
3.判断购物车有没有这个物品
4.跳转到对应的jsp页面去

二、购物车例子(Servlet实现)

jsp代码如下(使用三个标签跳转)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<a href="CarServlet?id=0"><h3>Iphone 8</h3></a><br>
<a href="CarServlet?id=1"><h3>小米6</h3></a><br>
<a href="CarServlet?id=2"><h3>三星Note8</h3></a><br>
<a href="CarServlet?id=3"><h3>魅族7</h3></a><br>
<a href="CarServlet?id=4"><h3>华为9</h3></a><br>

</body>
</html>

CarServlet代码如下:

map在session里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class CarServlet extends HttpServlet {	
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");

//1.先获取要添加到购物车的商品id
int id = Integer.parseInt(request.getParameter("id"));
String[] names= {"Iphone 8","小米6","三星Note8","魅族7","华为9"};
//获取到id对应的商品名字
String name=names[id];

//2.获取购物车存放东西的session-----Map<String ,Integer> 比如Iphone7 3
Map<String,Integer> map=(Map<String, Integer>) request.getSession().getAttribute("cart"); //取值getAttribute
if(map==null)
{
map=new LinkedHashMap<String,Integer>();
request.getSession().setAttribute("cart", map); //存值
}

//3.判断购物车有没有这个物品
if(map.containsKey(name)) //map里面有一个遍历的方法
{
//在原来基础上+1
map.put(name, map.get(name)+1);
}
else
{
//从来没买过直接设置为1
map.put(name, 1);
}

//4.输出界面(跳转)
response.getWriter().write("<a href='product_list.jsp'><h3>继续购物</h3></a>");
response.getWriter().write("<a href='cart.jsp'><h3>去购物车结算</h3></a>");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

}

点击对应的名字之后会弹出:


三、 展示购物车的东西:#

刚才就是点进去之后我们需要实现去购物车结算的功能,因此完成cart.jsp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.Map"%>
<%@page import="java.util.Map"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h2>您的购物车的商品如下:</h2>
<%
//1.先获取map
Map<String,Integer> map=(Map<String,Integer>)session.getAttribute("cart");

//2.遍历map
if(map!=null)
{
for(String key:map.keySet()) //map.keySet()获取所有
{
int value=map.get(key);
//key:商品名称
//value:商品个数
//<h3>名称:Iphone 数量:6</h3>
%>
<h3>名称:<%=key %> 数量:<%=value %></h3> <br>
<%
}
}
%>

</body>
</html>

一开始我们设定一定量的苹果手机数量,然后点击小米-点击继续购物(添加到里面)-不停的重复-最终可以选择去购物车结算


四、清空购物车#

  1. 在cart.jsp页面里面加一行清空购物车的按钮(按到展示页面之后可以选择清空购物车按钮,然后按钮可以跳转到clearCartServlet类,然后完成功能)
1
<a href="clearCartServlet"><h4>清空购物车</h4></a>
  1. 而clearCartServlet类需要完成获取session然后移除掉之后可以重定定位跳转到product_list.jsp页面重新选择

清空session两种方式:

1
2
3
4
5
6

//1.强制干掉会话,里面存放的任何数据就都没有了。
session.invalidate();

//2.从session中移除某一个数据
//session.removeAttribute("cart");

clearCartServlet类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class clearCartServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//获取session
HttpSession session = request.getSession();

//移除值
session.removeAttribute("cart");

//使用重定位
response.sendRedirect("product_list.jsp"); //跳到product_list.jsp
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

五、完整分析和整理:

代码框架:

  • product_list.jsp页面:主要是用来写首页的样子(手机名称)
  • CarServlet类:主要是用来完成具体的操作;主要是完成点击商品之后获取商品id一存放商品,判断有没有然后增加可以跳转到继续购物(继续跳到首页)/购物车结算(cart.jsp页面)。
  • cart.jsp页面:主要是完成跳转后看到购物车里面有哪些东西,然后可以选择清空购物车
  • clearCartServlet类:主要是完成cart.jsp页面内的清空购物车,然后退出到首页。

六、具体代码如下

product_list.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<a href="CarServlet?id=0"><h3>Iphone 8</h3></a><br>
<a href="CarServlet?id=1"><h3>小米6</h3></a><br>
<a href="CarServlet?id=2"><h3>三星Note8</h3></a><br>
<a href="CarServlet?id=3"><h3>魅族7</h3></a><br>
<a href="CarServlet?id=4"><h3>华为9</h3></a><br>

</body>
</html>

首页效果如下:

CarServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class CarServlet extends HttpServlet {	
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");

//1.先获取要添加到购物车的商品id
int id = Integer.parseInt(request.getParameter("id"));
String[] names= {"Iphone 8","小米6","三星Note8","魅族7","华为9"};
//获取到id对应的商品名字
String name=names[id];

//2.获取购物车存放东西的session-----Map<String ,Integer> 比如Iphone7 3
Map<String,Integer> map=(Map<String, Integer>) request.getSession().getAttribute("cart"); //取值getAttribute
if(map==null)
{
map=new LinkedHashMap<String,Integer>();
request.getSession().setAttribute("cart", map); //存值
}

//3.判断购物车有没有这个物品
if(map.containsKey(name)) //map里面有一个遍历的方法
{
//在原来基础上+1
map.put(name, map.get(name)+1);
}
else
{
//从来没买过直接设置为1
map.put(name, 1);
}

//4.输出界面(跳转)
response.getWriter().write("<a href='product_list.jsp'><h3>继续购物</h3></a>");
response.getWriter().write("<a href='cart.jsp'><h3>去购物车结算</h3></a>");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

}

商品点击后的二级页面如下:

cart.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.Map"%>
<%@page import="java.util.Map"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h2>您的购物车的商品如下:</h2>
<%
//1.先获取map
Map<String,Integer> map=(Map<String,Integer>)session.getAttribute("cart");

//2.遍历map
if(map!=null)
{
for(String key:map.keySet())
{
int value=map.get(key);
//key:商品名称
//value:商品个数
//<h3>名称:Iphone 数量:6</h3>
%>
<h3>名称:<%=key %> 数量:<%=value %></h3> <br>
<%
}
}
%>

<a href="clearCartServlet"><h4>清空购物车</h4></a>

</body>
</html>

商品点击清空购物车的页面如下:

clearCartServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class clearCartServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//获取session
HttpSession session = request.getSession();
//移除值
session.removeAttribute("cart");
//使用重定位
response.sendRedirect("product_list.jsp"); //跳到product_list.jsp
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

,