王者商店之jdbc—Properties读取配置文件,MVC设计模式,MySQL建表,Statement和PreparedStatement的区别及用法
今天咱们要分享的是一个控制台版本的具有管理员和普通用户操作的王者商店系统,因为Java代码较多,所以分开来,望大家理解
因为本人之前分享过了一次jdbc连接的步骤及使用,不会jdbc的好兄弟们,可以点(jdbc连接数据库的一般步骤)进行观看学习,仅供参考
下面进入今天的主题:王者商店,今天咱们的任务是:完成初始化界面和管理员功能
做项目之前,先分析思路,所以我列了一个大纲,仅供大家参考,本人也是根据此大纲进行编写的功能,有不足的地方欢迎大家加下面微信进行探讨:
因为在咱们开发中,一般情况下是尽量避免来修改源代码的,所以咱们的jdbc的一般配置步骤都写在配置文件中,所以我把配置全部写在properties文件中,简单的来看下MVC设计模式,咱们先来大致看一下项目结构
接着说下MVC三层架构设计模式
M:Model | 数据模型层 | 简称一个功能,用JavaBean实现 |
---|---|---|
V:View | 视图层 | 用于展示,以及与用户交互.可以使用html,css,js,jquery等前端技术来实现 |
C:Controller | 控制层 | 接受请求,将请求跳转到模型Model中进行处理,模型Model处理完毕后,再将处理结果返回给请求出.可以使用jsp实现,但是一般建议使用servlet实现控制器 |
然后第一步就来配置properties文件,我命令的是sql.properties:
user=rootpassword=123456url=jdbc:mysql://localhost:3306/wzry?useSSL=false&serverTimezone=UTCdriver=com.mysql.cj.jdbc.Driver
第二步,然后开始写工具类—在util包下面:
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class DBUtil {/**连接数据库的用户名*/// private static final String USER = \"root\";/**连接数据库的密码*/// private static final String PASSWORD = \"123456\";/**连接数据库的驱动类*/// private static final String DRIVER = \"com.mysql.cj.jdbc.Driver\";/**连接数据库的*/// private static final String URL=\"jdbc:mysql://localhost:3306/wzry?useSSL=false&serverTimezone=UTC\";//以上注释部分代码为jdbc基本配置,如果不想写properties文件的话,可以直接在工具类里面直接这样配置/**创建Properties对象*/static Properties pro = new Properties();/*** 使用静态代码块*静态代码块的作用是:随着类的加载而记载,并且只加载一次* */static {try {//加载配置文件,注意文件的路径pro.load(new FileInputStream(\"mysql.properties\"));} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 获取连接* @return*/public static Connection getcon() {try {/*** 加载驱动类* 我这里使用的是8.0的数据库,可以不写驱动类,所以我直接注释了* 如果用的5的数据库记得加上驱动类* */// Class.forName(DRIVER);//建立连接//pro.getProperty();是获取properties文件中的字段的值Connection con = DriverManager.getConnection(pro.getProperty(\"url\"), pro.getProperty(\"user\"), pro.getProperty(\"password\"));//将对象返回return con;}// catch(ClassNotFoundException e) {// e.printStackTrace();// }catch (SQLException e) {e.printStackTrace();}return null;}/*** 释放资源* @param con* @param pstmt* @param rs*/public static void closeResource(Connection con ,Statement stmt,ResultSet rs) {try {//如果不为空,则关闭资源//先开的后关,后用的先关if(rs != null) rs.close();if(stmt != null) stmt.close();if(con != null) con.close();} catch (SQLException e) {e.printStackTrace();}}public static void closeResource(Connection con ,Statement stmt) {//递归closeResource(con, stmt, null);}}
第三步:创建数据库,数据表:
Hero表用来存放英雄,user表用来存放用户,user_hero表用来存放用户购买过哪些英雄
第四步:创建实体类,在entity包下,创建User类,Hero类UserHero类
注意:实体类的属性要和数据库中的字段一一对应,实体类也称为JavaBean,也是Java三大特性的封装
//创建user类public class User {private int id;//用户idprivate String username;//用户名private String password;//密码private double money;//钱private int UserType;//用户类型,用与区分管理员和普通用户public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}public int getUserType() {return UserType;}public void setUserType(int userType) {UserType = userType;}}
//常见Hero表public class Hero {private int id;//idprivate String name;//英雄名字private String job;//职位,属性private int level;//等级private double price;//价格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 String getJob() {return job;}public void setJob(String job) {this.job = job;}public int getLevel() {return level;}public void setLevel(int level) {this.level = level;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public Hero(String name, String job, int level, double price) {super();this.name = name;this.job = job;this.level = level;this.price = price;}public Hero() {super();}}
//创建UserHero表public class UserHero {private int id;//编号private String username;//用户名private String name;//英雄名称private double price;//英雄价格public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}}
第五步,把基本界面搭建出来,在控制层controller包下创建一个类,我创建的是MainTest类
//创建MainTest类import java.util.Arrays;import java.util.List;import java.util.Scanner;import com.ltq.dao.HeroDao;import com.ltq.dao.UserDao;import com.ltq.dao.UserHeroDao;import com.ltq.entity.Hero;import com.ltq.entity.User;import com.ltq.entity.UserHero;public class MainTest {//定义全局静态的扫描器static Scanner in = new Scanner(System.in);//创建一个User对象,用来存放登陆的用户static User loginUser = null;public static void main(String[] args) {//死循环,让程序一直运行下去while(true) {//欢迎方法welcome();//用于接收用户输入数,用于判断直接不同的操作int num = in.nextInt();switch(num) {case 1://如果有用户名和密码可以直接登陆login();break;case 2://没有用户名和密码可以来注册regist();break;case 3://可以查看英雄showHero();break;default:System.out.println(\"您输入的指令不正确,请重新输入~~~~~\");break;}}}/*** 欢迎界面*/private static void welcome() {System.out.println(\"~~~欢迎来到王者商店~~~\");System.out.println(\"-----------------\");System.out.println(\"~~~请输入您的操作:~~~\");System.out.println(\"1.登陆 2.注册 3.查看英雄\");}/*** 登陆功能*/private static void login() {//判断用户是否登陆过,因为上面定义的为nullif(loginUser == null) {System.out.println(\"请输入用户名:\");String uname = in.next();System.out.println(\"请输入密码:\");String upwd = in.next();//创建UserDao对象UserDao user = new UserDao();//在UserDao类中创建findUser方法传入用户输入的用户名和密码在数据库中查找,返回User对象User findUser = user.findUser(uname, upwd);//如果返回的对象为null的话,说明在数据库中没有找到,没有找到说明输入的用户名密码错误,则不能登陆,提示登陆失败if(findUser != null) {//登陆成功的话将返回的User对象赋值给定义的loginUser,表示登陆成功,loginUser = findUser;//获取登陆用户的身份类型//如果是1的话表示是管理员//不等于1的话表示普通用户if(loginUser.getUserType()==1) {//登陆成功,获取登陆的用户名System.out.println(\"登陆成功,欢迎您管理员------\"+loginUser.getUsername()+\"\\n\");//查看管理员的菜单showAdminMenu();}else {//登陆成功,获取登陆的用户名System.out.println(\"登陆成功,欢迎您:------\"+loginUser.getUsername()+\"\\n\");//查看普通用户的菜单,这个明天来做showUserMenu(findUser);}}else {System.out.println(\"登录失败\");}}else {//如果已经登陆的话,提示,您已登录System.out.println(\"您已登录\"+loginUser.getUsername());}}/*** 管理员用户查看的菜单*/private static void showAdminMenu() {while(true) {System.out.println(\"请按照以下指令进行操作:\");System.out.println(\"1.查看英雄 2.添加英雄 3.删除英雄 4.修改密码 5.退出\");int num = in.nextInt();switch(num) {case 1://查看英雄showHero();break;case 2://插入英雄insertHero();break;case 3://删除英雄deleteHero();break;case 4://通过用户名修改密码updateAdminToPwd();break;case 5://将登陆用户设置为空loginUser = null;//终止循环return;default:System.out.println(\"您输入的指令有误.......\");}}}/*** 管理员查看英雄*/private static void showHero() {//创建HeroDao对象HeroDao hd = new HeroDao();//创建findAllHero方法,返回List集合List<Hero> allHeros = hd.findAllHero();System.out.println(\"本店现有英雄有:\");//遍历集合,将英雄全部遍历出来,然后打印for(Hero h : allHeros) {System.out.println(\"编号:\"+h.getId()+\",名称:\"+h.getName()+\",职位:\"+h.getJob()+\",等级:\"+h.getLevel()+\",价格:\"+h.getPrice());}}/*** 管理员添加英雄*/private static void insertHero() {System.out.println(\"请输入英雄的名字:\");String name = in.next();System.out.println(\"请输入英雄的职位:\");String job = in.next();System.out.println(\"请输入英雄的等级:\");int level = in.nextInt();System.out.println(\"请输入英雄的价格:\");double price = in.nextDouble();//创建hero对象,将管理员输入的信息保存到实体类中,进行暂时的存储,实现传递Hero hero = new Hero(name,job,level,price);HeroDao hd = new HeroDao();//在HeroDao对象的类中创建insertHero方法,然后将Hero对象传递过来//返回的是数据库中sql语句影响的行数int i = hd.insertHero(hero);//如果影响的行数大于0的话,则表示添加成功//因为在数据库中,如果成功插入一行的话,会返回1,不成功的话则是0if(i>0) {System.out.println(\"英雄添加成功~~~~\");}else{System.out.println(\"英雄添加失败.....\");}}/*** 管理员删除英雄*/private static void deleteHero() {//当管理员想要删除英雄的时候需要先查看英雄的id编号,所以我先将所有的英雄显示出来showHero();System.out.println(\"请输入英雄的编号:\");int num = in.nextInt();//创建HeroDao对象HeroDao hd = new HeroDao();//删除和插入操作相同,返回值都是sql语句影响的行数int i = hd.deleteByidToHero(num);if(i>0) {System.out.println(\"英雄删除成功~~~\");}else {System.out.println(\"英雄删除失败---\");}}/*** 修改管理员密码*/private static void updateAdminToPwd() {System.out.println(\"请输入您的旧密码:\");String pwd = in.next();UserDao ud = new UserDao();//这里面使用的是当前登陆的用户信息密码和用户输入的密码进行比较//如果相同的话表示是同一人可以进行修改,不相同则不可以修改if(pwd.equals(loginUser.getPassword())) {System.out.println(\"旧密码与新密码相匹配\");System.out.println(\"请输入新密码:\");String newPwd = in.next();//更新操作和删除插入一样,返回的都是sql语句影响的行数int newpwd = ud.updateByPwdToPassword(loginUser,newPwd);if(newpwd>0) {System.out.println(\"新密码修改成功~~~\");}else {System.out.println(\"密码修改失败.....\");}}else {System.out.println(\"旧密码有误\");}}
第六步,创建dao层—UserDao类和HeroDao类
//创建UserDaopublic class UserDao {/*** 根据用户名和密码进行查找用户* @param username* @param password* @return*/public User findUser(String username,String password) {//获取连接Connection con = DBUtil.getcon();//创建statement对象Statement st = null;//创建结果集对象ResultSet rs = null;//sql语句String sql = \"select * from user where username=\'\"+username+\"\'and password=\'\"+password+\"\'\";// System.out.println(sql);try {//实例化statement对象st = con.createStatement();//执行sql语句rs = st.executeQuery(sql);//创建user对象User user = null;//结果集对象的next()方法,返回是Boolean类型//类似于游标,一个一个进行查找,如果可以找到则是true表示可以继续查找,如果找不到了,表示查找完毕,返回false,结束循环while(rs.next()) {//实例化对象user = new User();//将找到的内容保存到user对象中,最终返回user.setId(rs.getInt(\"id\"));user.setMoney(rs.getDouble(\"money\"));user.setPassword(rs.getString(\"password\"));user.setUsername(rs.getString(\"username\"));user.setUserType(rs.getInt(\"UserType\"));}return user;} catch (SQLException e) {e.printStackTrace();}finally {//关闭资源DBUtil.closeResource(con, st, rs);}return null;}/*** 通过登陆的用户名来更新密码* @param pwd*/public int updateByPwdToPassword(User loginUser,String newPwd) {Connection con = DBUtil.getcon();//创建PreparedStatement对象,我这里使用了statement和PreparedStatement,稍后介绍两者的区别PreparedStatement pstmt = null;String sql = \"update user set password = ? where username = ?\";try {//预编译sql语句,防治sql注入pstmt = con.prepareStatement(sql);//为sql语句中的问号赋值pstmt.setString(1, newPwd);//获取当前登陆的用户的用户名,进行赋值pstmt.setString(2, loginUser.getUsername());//执行sql语句,返回的是sql语句影响的行数int i = pstmt.executeUpdate();//最终返回return i;} catch (SQLException e) {e.printStackTrace();}return 0;}}
//创建HeroDaopublic class HeroDao{/*** 查找所有英雄* @return*/public List<Hero> findAllHero() {Connection con = DBUtil.getcon();PreparedStatement pstmt = null;ResultSet rs = null;String sql = \"select * from hero\";try {pstmt = con.prepareStatement(sql);rs = pstmt.executeQuery();Hero hero = null;List<Hero> list = new ArrayList<Hero>();while(rs.next()) {hero = new Hero();hero.setId(rs.getInt(\"id\"));hero.setJob(rs.getString(\"job\"));hero.setLevel(rs.getInt(\"level\"));hero.setName(rs.getString(\"name\"));hero.setPrice(rs.getDouble(\"price\"));list.add(hero);}return list;} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.closeResource(con, pstmt, rs);}return null;}/*** 插入英雄* @param hero* @return*/public int insertHero(Hero hero) {Connection con = DBUtil.getcon();PreparedStatement pstmt = null;String sql = \"insert into hero(name,job,level,price) values(?,?,?,?)\";try {pstmt = con.prepareStatement(sql);pstmt.setString(1, hero.getName());pstmt.setString(2, hero.getJob());pstmt.setInt(3, hero.getLevel());pstmt.setDouble(4, hero.getPrice());int i =pstmt.executeUpdate();return i;} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.closeResource(con, pstmt);}return 0;}/*** 通过id删除英雄*/public int deleteByidToHero(int num) {Connection con = DBUtil.getcon();String sql = \"delete from hero where id = ?\";PreparedStatement pstmt = null;try {pstmt = con.prepareStatement(sql);pstmt.setInt(1, num);int i = pstmt.executeUpdate();return i;} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.closeResource(con, pstmt);}return 0;}}
statement和PreparedStatement两者的区别:
statement有sql注入,比如在查询语句输入以下命令:
select * from user where username=\'甜甜\' and password=\'123\' or \'2\'=\'2\';
写段代码进行测试后的效果:
因为sql语句最后拼接出来的or表示两个条件只要满足一个条件即可,那么’2’=\’2’肯定是恒成立的,从而达到了破坏sql语句执行效果,在开发中我们应尽量杜绝sql注入…
PreparedStatement预编译sql语句,可以有效的防止sql注入
因为PreparedStatement预编译语句是利用\”?\”占位符来占据位置,表示此处需要传值,在预编译后进行传值,从而避免了sql注入现象
String sql = \"select * from user where username=\'\"+username+\"\' and password=\'\"+pwd+\"\'\";-- 可以修改为:String sql = \"select * from user where username=? and password=?\";
可以通过PreparedStatement来编译sql语句:
PreparedStatement pst = con.prepareStatement(sql);
编译sql语句之后,因为sql语句中有?那么就需要给?设置值。设置值时可以采用
setInt(index,value), 设置int类型的值给第index个?问号
setString(index,value),设置String类型的值给第index个?问号
setDouble(index,value),设置double类型的值给第index个?问号
//如果sql语句中有? ,就要给?配置值// pst.setXXX(第几个? ,需要赋值给这个?得值)//XXX:表示,?所对应值的 数据类型pst.setString(1,username);pst.setString(2, pwd);
因为PreparedStatement预编译语句是利用\”?\”占位符来占据位置,表示此处需要传值,在预编译后进行传值,从而避免了sql注入现象
String sql = \"select * from user where username=\'\"+username+\"\' and password=\'\"+pwd+\"\'\";-- 可以修改为:String sql = \"select * from user where username=? and password=?\";
可以通过PreparedStatement来编译sql语句:
PreparedStatement pst = con.prepareStatement(sql);
编译sql语句之后,因为sql语句中有?那么就需要给?设置值。设置值时可以采用
setInt(index,value), 设置int类型的值给第index个?问号
setString(index,value),设置String类型的值给第index个?问号
setDouble(index,value),设置double类型的值给第index个?问号
//如果sql语句中有? ,就要给?配置值// pst.setXXX(第几个? ,需要赋值给这个?得值)//XXX:表示,?所对应值的 数据类型pst.setString(1,username);pst.setString(2, pwd);
管理员功能做出来了,本人尽快更新普通用户功能
不要失去信心,只要坚持不懈,就终会有成果的,加油—正在为梦想拼搏的兄弟们!!!