接著前面那個demo講,之前我們沒有引入數(shù)據(jù)庫,這里我們加入數(shù)據(jù)庫MySQL疹瘦。(工程day14_user
)
一、建立數(shù)據(jù)庫
CREATE TABLE `users` (
`id` varchar(40) NOT NULL,
`username` varchar(40) NOT NULL,
`password` varchar(40) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`birthday` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
二巡球、改造dao層
之前我們dao層實(shí)現(xiàn)是使用的XML充當(dāng)?shù)臄?shù)據(jù)庫言沐,于是操作的是XML文檔邓嘹,這里我們需要將dao層實(shí)現(xiàn)換成操作數(shù)據(jù)庫。
UserDaoJdbcImpl.java
package cn.itcast.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import cn.itcast.utils.JdbcUtils;
import cn.itcast.utils.XmlUtils;
public class UserDaoJdbcImpl implements UserDao {
//使用用戶名和密碼去查找险胰,用于用戶登錄
@Override
public User find(String username , String password) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet result = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where username=? and password=?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
result = ps.executeQuery();
if(result.next()){
User user = new User();
user.setId(result.getString("id"));
user.setUsername(result.getString("username"));
user.setPassword(result.getString("password"));
user.setEmail(result.getString("email"));
user.setBirthday(result.getDate("birthday"));
return user;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtils.release(conn, ps, result);
}
return null;
}
//使用用戶名去查找汹押,用于用戶注冊,查看系統(tǒng)中此用戶名是否被使用
@Override
public User find(String username){
Connection conn = null;
PreparedStatement ps = null;
ResultSet result = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where username=?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
result = ps.executeQuery();
if(result.next()){
User user = new User();
user.setId(result.getString("id"));
user.setUsername(result.getString("username"));
user.setPassword(result.getString("password"));
user.setEmail(result.getString("email"));
user.setBirthday(result.getDate("birthday"));
return user;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtils.release(conn, ps, result);
}
return null;
}
//添加用戶
@Override
public void add(User user){
Connection conn = null;
PreparedStatement ps = null;
ResultSet result = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into users(id, username, password, email, birthday) values(?,?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getId());
ps.setString(2, user.getUsername());
ps.setString(3, user.getPassword());
ps.setString(4, user.getEmail());
//設(shè)置日期注意java中和數(shù)據(jù)庫中是不同的
ps.setDate(5, new java.sql.Date(user.getBirthday().getTime()));
ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtils.release(conn, ps, result);
}
}
}
說明:這里我們不再使用Statement這個類了起便,這個類使用起來較為麻煩棚贾,我們使用其子類PreparedStatement,使用較為簡單榆综,這里就不多說了妙痹。
JdbcUtils.java
package cn.itcast.utils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
public class JdbcUtils {
private static String driver;
private static String url ;
private static String username;
private static String password;
static {
try{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username, password);
}
public static void release(Connection conn, PreparedStatement ps, ResultSet result){
if(result != null){
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
三、改造service層
在之前的service層中我們使用的是UserDaoXmlImpl.java
,這里我們使用UserDaoJdbcImpl.java
鼻疮,于是只需要將
UserDao dao = new UserDaoXmlImpl();
換成
UserDao dao = new UserDaoJdbcImpl();
即可怯伊,但是假如以后我們不想更改service層,則可以使用工廠設(shè)計(jì)模式陋守,讓工廠幫我們實(shí)現(xiàn)好一個UserDao震贵,我們直接拿過來用即可,如:
UserDao dao = DaoFactory.getInstance().createUserDao();
工廠設(shè)計(jì)模式其實(shí)就是去讀取配置文件水评,根據(jù)配置文件來實(shí)現(xiàn)哪個dao實(shí)現(xiàn)猩系,這里給出配置文件:
dao.properties
#userdao=cn.itcast.dao.impl.UserDaoXmlImpl
userdao=cn.itcast.dao.impl.UserDaoJdbcImpl
工廠方法(DaoFactory.java
)
package cn.itcast.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.dao.UserDao;
public class DaoFactory {
//工廠設(shè)計(jì)模式一般是單例的
private static UserDao userDao = null ;
private DaoFactory(){
//我們一般將只執(zhí)行一次的代碼放在靜態(tài)代碼塊中,但是這里的工廠是單例的中燥,所以我們還可以
//將這樣的代碼放在其構(gòu)造函數(shù)中
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
Properties properties = new Properties();
try {
properties.load(in);
String daoClassName = properties.getProperty("userdao");
userDao = (UserDao) Class.forName(daoClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static final DaoFactory instance = new DaoFactory();
public static DaoFactory getInstance(){
return instance;
}
public UserDao createUserDao(){
return userDao;
}
}
這樣我們在以后如果想跟換數(shù)據(jù)庫就只需要改一下配置文件即可寇甸。
最后:
上面我們加入數(shù)據(jù)庫之后我們是使用工廠設(shè)計(jì)模式幫我們new一個UserDaoImpl,用于操作數(shù)據(jù)庫疗涉,那同樣我們以后service層也可能變化那我們同樣可以通過工廠設(shè)計(jì)模式讓工廠幫我們實(shí)現(xiàn)一個業(yè)務(wù)類讓我們的servlet使用拿霉,其實(shí)在以后使用spring之后會發(fā)現(xiàn),其實(shí)spring就是做這樣的工作咱扣,它會幫我們實(shí)現(xiàn)好業(yè)務(wù)類和dao實(shí)現(xiàn)讓我們使用绽淘,我們?nèi)绻弥苯诱襰pring拿就好了,spring會幫我們注入進(jìn)來闹伪。而struts的功能就是實(shí)現(xiàn)web層的解耦沪铭,用戶的請求過來之后去找struts的action,然后action去調(diào)用相關(guān)的業(yè)務(wù)類偏瓤,而相關(guān)的業(yè)務(wù)類實(shí)現(xiàn)讓spring注入進(jìn)來杀怠,這就是struts的功能。而Hibernate的功能就是操作數(shù)據(jù)庫厅克。這樣我們對三大框架的理解就容易多了赔退。
補(bǔ)充:防sql注入。
如果我們使用的是Statement
對象,在登錄時在用戶名處填寫'or 1=1 or username='
硕旗,則會登錄成功窗骑,這是應(yīng)為將這條字符串發(fā)送到服務(wù)器后被組裝成了下面這條sql語句:
select * from users where username='' or 1=1 or username='' and password=''
這條語句就相當(dāng)于select * from where true;
這樣會以第一個用戶登錄成功卵渴。解決辦法是使用預(yù)編譯對象PreparedStatement
慧域。這個對象是Statement
的子對象鲤竹,它的實(shí)例對象可以通過調(diào)用Connection.preparedStatement()
方法獲得浪读,相對于Statement
對象而言,PreparedStatement
可以避免sql注入的問題。因?yàn)檫@調(diào)語句已經(jīng)預(yù)編譯了辛藻,之后傳進(jìn)來的字符串預(yù)編譯之后則不會造成之前的那種情況碘橘,這樣就預(yù)防了注入攻擊。同時吱肌,預(yù)編譯之后數(shù)據(jù)庫就不需要再次編譯痘拆,可以直接執(zhí)行,這樣就減輕了數(shù)據(jù)庫的壓力氮墨,提升了性能纺蛆。以后都使用此對象。