SSH框架是每个学生毕业前都必须掌握的一门技术,所以这里就用Spring的JDBC模板和自定义异常让事务回滚来做一个练习。主要用到的是spring和struts2框架来操作,这里暂时没用Hibernate来对数据进行操作。
一、练习要求。
要求写一个用户购买股票的项目,用户先开户后登陆,可以在里面买股票和卖股票,当买股票金额超过用户金额时就报异常让事务回滚,用户卖出股票时超过持有数量报异常让事务回滚。
二、数据库表
这里因为没有用到Hibernate所以不能通过配置实体类创建表,所以只能手动创建,表结构如下:
person表;
stock表
三、创建包和配置好所需的配置文件
先导入spring和struts2所需要用到的包,再创建配置文件,然后我们再新建对应的Package。
四、开户和登录
买或卖股票之前每个人都有自己的账户来存自己的金额和信息,这里先设计好jsp页面让用户进行选择是开户还是登录。(这里只是简单的弄了下jsp页面)
点击开户就进到另一个jsp页面来进行操作
到写数据操作语句,这里要先对用户名进行排重下,以免重复账户。先到Dao层写好接口,让DaoImp层实现接口。
DaoImp实现接口时也要继承下JdbcDaoSupport这样才能对数据进行处理。
public class ZhuCeDaoImp extends JdbcDaoSupport implements ZhuCeDao{
@Override
public boolean addUser(Person p) {
System.out.println("进入开户~~~");
//排重,如果为true就直接返回false;
if(this.touser(p.getUserName())){
return false;
}
//如果排除为false,就好进行添加用户,这里我们传实体类就可以获取到对应的参数。
String sql = "insert into person(username,password,money) value (?,?,?)";
//直接this关键字调用继承父类的方法来操作sql语句
int i = this.getJdbcTemplate().update(sql,p.getUserName(),p.getPassWord(),p.getMoney());
if(i!=0){
//判断下,在后台输出看是否成功
System.out.println("开户成功");
return true;
}else{
System.out.println("开户失败");
return false;
}
}
//用户名排重
public boolean touser(String name){
System.out.println("进入排重~~~");
//查找对应的用户名
String sql = "select username from person where username=?";
List s = this.getJdbcTemplate().queryForList(sql, String.class,name);
//存的是List类型,所以list不为空就是该用户名已存在,反之则没有
if(s.size()!=0){
return true;
}
return false;
}
}
再写Service层接口,让ServiceImp层实现,Service层接口的方法就是Dao层接口的方法,所以为了方便集中把所以的Dao层接口方法都写在了一个Service层接口里面,
ServiceImp层也是直接调用Dao层对象来实现全部方法。
Service层
public interface TestService {
//用户开户
public boolean addUser(Person p);
//用户登录
public boolean toLogin(Person p);
//买股票
public boolean tobuy(String stockName,String username,int count) throws MyException;
//卖股票
public boolean tosell(String stockName,String username,int count) throws MyException;
//获取登录用户的所以股票信息
public List<Stock> getUser(String userName);
//获取登录用户的总价
public double getMoney(String userName);
}
当用户填写完form表单后点击开户提交就会通过struts2调用对应的action层,action层再调用Service层来对数据进行操作,要配置好spring配置文件。
public class ZhuCeKZ extends ActionSupport{
//创建实体类,通过反射可以获取到form表单的提交数据,
//但form表单的名字必须和实体类属性名相同,提供set,get方法,
private Person p;
public String toZhuCe(){
//spring自动创建Service对象
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
TestService ts = (TestService) ac.getBean("textS");
//调用开户处理,true就跳到登录视图,false就跳回注册视图重新注册
if(ts.addUser(p)){
return "success";
}else{
return "shibai";
}
}
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
}
成功注册后,就进入登录的jsp页面。
也是先写好Dao层接口和让DaoImp实现,再Service层接口让ServiceImp实现
Dao层
public interface LoginDao {
public boolean toLogin(Person p);
}
DaoImp层
public class LoginDaoImp extends JdbcDaoSupport implements LoginDao{
@Override
public boolean toLogin(Person p) {
System.out.println("进入登录~~~");
//先根据用户名查下是否存在
String sql = "select * from person where username=?";
//因为Person不是基本数据类型,所以要先自定义类型下
RowMapper<Person> rowMapper = new BeanPropertyRowMapper<Person>(Person.class);
List<Person> list = this.getJdbcTemplate().query(sql, rowMapper,p.getUserName());
//存在List里面,判断下为空表示没有改数据
if(list.size()!=0){
//不为空的话就让查询到的实体和登录的实体密码就行判断
if(list.get(0).getPassWord().equals(p.getPassWord())){
return true;
}
}
return false;
}
}
form表单提交跳到对应Action层,调用Service对象操作。
public class LoginKZ extends ActionSupport{
private Person p;
public String toLogin(){
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
TestService ts = (TestService) ac.getBean("textS");
if(ts.toLogin(p)){
//把用户名存进session里面在页面显示下,和后面方便数据存取
ActionContext.getContext().getSession().put("username", p.getUserName());
return "success";
}
return "shibai";
}
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
}
五、股票的买和卖
成功登录后就会跳到一个功能界面,用来购买股票和查看用户持有股。
当点击股票市场时就会进入查看股票价格,买卖股票可以输入对应的数量,所以用了些js来操作。
先写好Dao层的数据操作。
Dao层接口
public interface TradeDao {
//买股票
public boolean tobuy(String stockName,String username,int count);
//卖股票
public boolean tosell(String stockName,String username,int count);
//获取登录用户的所以股票信息
public List<Stock> getUser(String userName);
//获取登录用户的总价
public double getMoney(String userName);
}
DaoImp层
public class TradeDaoImp extends JdbcDaoSupport implements TradeDao{
//买股票处理
@Override
public boolean tobuy(String stockName, String username, int count) {
System.out.println(username+"买"+stockName+"股票数量:"+count);
//调用写好的排除股票重复方法
if(this.getBuy(stockName, username)){
//重复就直接更新股票数量
String sql = "update stock set number=number+? where stockName=? and userName=?";
this.getJdbcTemplate().update(sql,count,stockName,username);
//调用写好的(购买股票数量*股票价格)方法,来进行对用户金额的加减
double money = this.zeng(count, stockName);
String sql2 = "update person set money=money-? where userName=?";
this.getJdbcTemplate().update(sql2,money,username);
System.out.println("购买股票成功");
return true;
}
//如果排重为false就是新购买,所以要新添加股票信息进数据库
String sql = "insert into stock(stockName,userName,number) value(?,?,?)";
int i = this.getJdbcTemplate().update(sql,stockName,username,count);
//如果成功就调用写好的(购买股票数量*股票价格)方法,来进行对用户金额的加减
if(i!=0){
double money = this.zeng(count, stockName);
String sql2 = "update person set money=money-? where userName=?";
this.getJdbcTemplate().update(sql2,money,username);
System.out.println("购买股票成功");
return true;
}
System.out.println("购买股票失败");
return false;
}
//卖股票处理
@Override
public boolean tosell(String stockName, String username, int count) {
System.out.println(username+"卖"+stockName+"股票数量:"+count);
//调用写好的方法,判断卖出数量是否超过持有数,超过就直接返回false
int cou = this.getStock(stockName, username);
if(cou<count){
return false;
}
//调用排重方法,false就是没持有改股票,true就是持有该股票
if(this.getBuy(stockName, username)){
//持有股票数量减去卖出数量
String sql = "update stock set number=number-? where stockName=? and userName=?";
this.getJdbcTemplate().update(sql,count,stockName,username);
//调用写好的(购买股票数量*股票价格)方法,来进行对用户金额的加减
double money = this.zeng(count, stockName);
String sql2 = "update person set money=money+? where userName=?";
this.getJdbcTemplate().update(sql2,money,username);
return true;
}
return false;
}
//遍历出用户股票数量
@Override
public List<Stock> getUser(String userName) {
System.out.println("遍历用户股票");
String sql = "select * from stock where userName=?";
//因为Stock不是基本数据类型,所以要先自定义类型下
RowMapper<Stock> rowMapper = new BeanPropertyRowMapper<Stock>(Stock.class);
List<Stock> list =this.getJdbcTemplate().query(sql,rowMapper,userName);
return list;
}
//获取剩余资产
@Override
public double getMoney(String userName) {
String sql = "select money from person where userName=?";
double money = this.getJdbcTemplate().queryForObject(sql, Double.class,userName);
return money;
}
//自定义方法
//股票排重
public boolean getBuy(String stockName, String username){
String sql = "select * from stock where stockName=?";
RowMapper<Stock> rowMapper = new BeanPropertyRowMapper<Stock>(Stock.class);
List<Stock> list = this.getJdbcTemplate().query(sql,rowMapper,stockName);
if(list.size()!=0){
for(int i=0;i<list.size();i++){
if(list.get(i).getUserName().equals(username)){
return true;
}}}
return false;
}
//判断类型对money进行加减
public double zeng(int count,String type){
double i = 0;
//因为这里没存进数据库,所以直接写死
if(type.equals("华为")){
i = count*200;
}
if(type.equals("网易")){
i = count*300;
}
if(type.equals("三星")){
i = count*20;
}
return i;
}
//获取已有的股票数,来判断是否超过卖出的数量
public int getStock(String stockName,String userName){
String sql = "select number from stock where stockName=? and userName=?";
int i = this.getJdbcTemplate().queryForInt(sql, stockName,userName);
return i;
}
}
当用户输入数量点击买卖股票时,就会跳到对应的Action层来调用Service对象,ServiceImp层直接调用Dao层对象来操作。
public class StockKZ {
//股票名,jsp页面传过来
private String stockName;
//股票数量,jsp页面传过来
private int count;
//登录用户持有股票
private ArrayList list;
//总价
private double money;
//定义成成员变量方便调用
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
//这里调用的是代理对象
TestService ts = (TestService) ac.getBean("serviceProxy");
//买股票控制
public String toBuy(){
//用户名在登录时已经存进session里面,所以可以获取到
String username = ActionContext.getContext().getSession().get("username").toString();
try {
if(ts.tobuy(stockName, username, count)){
return "success";
}
} catch (MyException e) {
e.printStackTrace();
}
return "shibai";
}
//卖股票控制
public String toSell(){
String username = ActionContext.getContext().getSession().get("username").toString();
try {
if(ts.tosell(stockName, username, count)){
return "success";
}
} catch (MyException e) {
e.printStackTrace();
}
return "shibai";
}
//查看资产和持股票数量
public String getZiCang() {
String userName = ActionContext.getContext().getSession().get("username").toString();
list = (ArrayList) ts.getUser(userName);
money = ts.getMoney(userName);
return "success2";
}
//set,get
public void setStockName(String stockName) {
this.stockName = stockName;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void setList(ArrayList list) {
this.list = list;
}
public ArrayList getList() {
return list;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
这样就可以买卖股票,在页面上显示登录用户持有股票和剩余金额
六、事务的回滚
如何让事务回滚,这里就要自定义异常,当有异常时就会让事务回滚,从而让数据没有改变。所以我们在买卖股票时加下判断,这里就要到ServiceImp层操作
public class TestServiceImp implements TestService{
//开户Dao
private ZhuCeDao zd;
//登录Dao
private LoginDao ld;
//股票操作Dao
private TradeDao td;
//用户开户
@Override
public boolean addUser(Person p) {
return zd.addUser(p);
}
//用户登录
@Override
public boolean toLogin(Person p) {
return ld.toLogin(p);
}
//买股票
@Override
public boolean tobuy(String stockName, String username, int count) throws MyException {
td.tobuy(stockName, username, count);
//获取下剩余的资产
double money = this.getMoney(username);
//如果小于0就抛异常,让事务回滚
if(money<0){
throw new MyException("你已经没有钱了~~~");
}
return true;
}
//卖股票
@Override
public boolean tosell(String stockName, String username, int count) throws MyException {
//获取下用户的全部持有股票信息
List list = this.getUser(username);
if(list.size()!=0){
//不为空就遍历
for(int i=0;i<list.size();i++){
Stock stock = (Stock) list.get(i);
//匹配股票名字是否相同,相同就判断用户持有该股票数量
//如果小于等于0就抛异常,让事务回滚
if(stockName.equals(stock.getStockName())&&stock.getNumber()<=0){
throw new MyException("你已经没有该公司的股票~~~");
}
}
}
return td.tosell(stockName, username, count);
}
//登录用户持有股票
@Override
public List<Stock> getUser(String userName){
List list = td.getUser(userName);
return list;
}
//获取总价
@Override
public double getMoney(String userName) {
return td.getMoney(userName);
}
//set
public void setZd(ZhuCeDao zd) {
this.zd = zd;
}
public void setLd(LoginDao ld) {
this.ld = ld;
}
public void setTd(TradeDao td) {
this.td = td;
}
}
如果抛出异常后台就会输出异常信息。
最后
spring的配置文件才是最重要的,所以来看下applicationContext.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="
http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注册数据源 (spring内置数据库源)-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 注册数据库属性文件 方式一 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 注册事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 生成service的事务代理对象 -->
<bean id="serviceProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="textS" />
<!-- 跟事务管理器关联 -->
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="to*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-MyException</prop>
</props>
</property>
</bean>
<!-- 注册Dao对象 -->
<!-- 开户Dao -->
<bean id="zhuceD" class="com.it.DaoImp.ZhuCeDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 登录Dao -->
<bean id="loginD" class="com.it.DaoImp.LoginDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 股票操作Dao -->
<bean id="tradeD" class="com.it.DaoImp.TradeDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注册Service对象 -->
<bean id="textS" class="com.it.ServiceImp.TestServiceImp">
<property name="zd" ref="zhuceD"></property>
<property name="ld" ref="loginD"></property>
<property name="td" ref="tradeD"></property>
</bean>
</beans>