一、需求
使用WebService+Hibernate+Oracle完成一下这几个需求
(刚到公司实习,老大让我先自学给了几个简单项目练手)
1、能录入同学成绩;
2、获取全班成绩列表;
3、计算全班成绩平均分;
4、查询单个同学单科成绩;
5、合理设计表结构,这个也会检查。
二、建库建表
(一)数据库表的设计:使用的Oracle数据库
学生表(STUDENT_USER)下面统称USER表:USER_ID为主键
课程表(STUDENT_COURSE)下面统称COURSE表:COURSE_ID为主键
(这里为什么有英文又要中文后面设计视图的时候分析)。
成绩表(STUDENT_GRADE)下面统称GRADE表(关联课程和学生表):
这里我用的是联合主键——(USER_ID,COURSE_ID),同时他们也是外键,分别关联USER表,COURSE表。USER_ID和USER表的约束为SET NULL 这样当我删除某条学生信息的时候,同时会删除该学生的所有信息。
(二)创建物化视图
为什么使用物化视图?(个人想法欢迎指点)
物化视图的更新十分影响性能,但是用它来查询数据,那么可以节省很多的时间。、
因为我觉得学生数据主要还是以查询为主,学生是没有权限去修改数据的,同时每次插入学生信息,一般就是在开学的时候,插入成绩就是在每次考试的时候,每次修改完半夜手动刷新就行了(我觉我在学校的成绩搞好几天才能查到这个原因),修改的次数比较少,所以我觉得建立一张物化视图,可以优化数据库,方便操作。
物化视图和普通视图的区别:
普通视图其实还是使用Sql查询,每次查询还是重复查询Sql,我觉得除了方便没有其他意义(不让程序员查看其他字段信息?)具体不是很清楚。
物化视图是直接建立一张物理表,存储在本地,等于生成了一张新的表,刷新的化一般就是手动刷新或者更改自动刷新,我这里使用的是自动刷新(主要是为了测试省事)
建立物化视图的SQl
SELECT
STUDENT_GRADE.USER_ID ID ,
STUDENT_USER.USER_NAME NAME,
STUDENT_USER.USER_SEX SEX,
SUM(CASE STUDENT_GRADE.COURSE_ID
WHEN 1 THEN STUDENT_GRADE.USER_GRADE
ELSE 0 END) LANGUAGE,
SUM(CASE STUDENT_GRADE.COURSE_ID
WHEN 2 THEN STUDENT_GRADE.USER_GRADE
ELSE 0 END) ENGLISH,
SUM(CASE STUDENT_GRADE.COURSE_ID
WHEN 3 THEN STUDENT_GRADE.USER_GRADE
ELSE 0 END) MATHEMATICS,
SUM(STUDENT_GRADE.USER_GRADE) TOTAL
FROM
STUDENT_USER JOIN STUDENT_GRADE
ON
STUDENT_USER.USER_ID = STUDENT_GRADE.USER_ID
GROUP BY STUDENT_USER.USER_NAME,
STUDENT_GRADE.USER_ID,
STUDENT_USER.USER_SEX
至于之前为什么课程表中需要有中文,英文就是这个物化视图中的字段,中文的化到时候代码返回,会用到,同时可以用中文关联去查询英文,在去查找需要的字段。
三、代码实现
(一)导入依赖
<dependencies>
<!-- 启动业务用的包 -->
<!-- Oracle 驱动-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.2.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!-- ws依赖 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 日志引入 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!-- spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- spring web集成 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- spring 整合junit -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- junit 开发包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<!-- 运行tomcat7方法:tomcat7:run -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>8080</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
(二) 文件配置
配置连接HIbernate
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 数据源配置-->
<property name="connection.username">LUNA_MCS_SXS</property>
<property name="connection.password">ewell</property>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@//192.168.(**).25:1521/EWELL</property>
<!-- c3po-->
<property name="hibernate.c3p0.acquire_increment">10</property>
<property name="hibernate.c3p0.idle_test_period">10000</property>
<property name="hibernate.c3p0.timeout">5000</property>
<property name="hibernate.c3p0.max_size">30</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_statements">10</property>
<!-- 设置方言-->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- 答应SQL语句-->
<property name="show_sql">true</property>
<!-- 格式化SQL-->
<property name="format_sql">true</property>
<!-- 配置在输出的SQL语句前面添加提示信息 -->
<property name="use_sql_comments">true</property>
<!-- 注册实体关系文件-->
<!-- <mapping resource="dao/xml/StudentCourse.hbm.xml"></mapping>-->
<mapping class="org.example.entity.StudentCourse"></mapping>
<mapping class="org.example.entity.StudentUser"></mapping>
<mapping class="org.example.entity.StudentGrade"></mapping>
<mapping class="org.example.entity.MVStudent"></mapping>
</session-factory>
</hibernate-configuration>
配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxfservlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<!--2.spring容器配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
配置 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:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<!--
Spring整合cxf发布基于restful风格服务,关键点:
1. 服务地址
2. 服务类
服务完整访问地址:http://localhost:8080/ws/serviceWs
-->
<jaxws:server address="/serviceWs">
<jaxws:serviceBean>
<bean class="org.example.service.impl.StudentServiceImpl"></bean>
</jaxws:serviceBean>
</jaxws:server>
</beans>
(三)创建关联表(没啥好说的,和视图还有表关联)
关联GRADE表(其他表基本都一样的)
@Data
@Accessors(chain = true)
@Entity
@Table(name = "STUDENT_GRADE")
public class StudentGrade implements Serializable {
@Id
@Column(name = "USER_ID")
private Integer userID;
@Id
@Column(name = "COURSE_ID")
private Integer courseID;
@Column(name = "USER_GRADE")
private Integer grade;
}
(四)需求实现
1.修改或者插入学生成绩:
思路的话用个对象接受学生信息,封装到映射学生表的类里面去,然后再把AllCourses对象里面的成绩拆离出来,插入到成绩表中。(挺简单的)
@WebMethod(operationName = "ModifyTheStudent")
public void setGrade(
@WebParam(name = "StudentInformation") StudentInformation studentInformation) {
//先拿到这个学生
StudentUser studentUser = new StudentUser();
studentUser.setId(studentInformation.getId());
studentUser.setName(studentInformation.getName());
studentUser.setSex(studentInformation.getSex());
//语文成绩
StudentGrade languageGrade = new StudentGrade();
languageGrade.setUserID(studentInformation.getId())//学生学号
.setCourseID(1)//课程id
.setGrade(studentInformation.getGrades().getLanguage());//课程成绩
//英语成绩
StudentGrade englishGrade = new StudentGrade();
englishGrade.setUserID(studentInformation.getId())
.setCourseID(2)
.setGrade(studentInformation.getGrades().getEnglish());
//数学成绩
StudentGrade mathematicsGrade = new StudentGrade();
mathematicsGrade.setUserID(studentInformation.getId())
.setCourseID(3)
.setGrade(studentInformation.getGrades().getMathematics());
session.merge(studentUser);
System.out.println(studentUser);
session.saveOrUpdate(languageGrade);
System.out.println(languageGrade);
session.saveOrUpdate(englishGrade);
System.out.println(englishGrade);
session.saveOrUpdate(mathematicsGrade);
System.out.println(mathematicsGrade);
session.beginTransaction().commit();
//一开始不清除缓存是无法修改的,会报:(A different object with the same identifier value was already associated with the session :
// [org.example.entity.StudentUser#65)的错误,是因为session中有原先这个id对象的缓存,相同id其他不同的对象无法加入,所以需要清楚一下缓存
session.clear();
}
首先我用一个StudentInformation 对象来接受传入的参数 :
StudentInformation : {
private Integer id; //学生的学号 private String name; //学生的名字 private String sex; //学生的性别 private AllCourses grades : { private Integer language; //学生的语文成绩 private Integer english; //学生的英语成绩 private Integer mathematics; //学生的数学成绩 }; }
一开始我是用一个Map集合接受学生的成绩的(其实后面很多返回值一开始都用了map),但是发现WebService好像不太建议使用map(有可能是我学艺不精,等以后慢慢学习),于是我该用对象接受。
然后session(因为没清除缓存)这边我也遇见了问题:见这篇博客
2.获取所有学生的成绩
思路:这个就更简单了,直接获取物化视图遍历出来,也没啥好说的
@WebMethod(operationName = "GetStudentGrades")
public @WebResult(name = "StudentGrades") List<MVStudent> getGrade() {
String hql = "from MVStudent ";
return session.createQuery(hql).list();
}
3.获取平均成绩
因为老大只告诉我说要获取平均成绩,我也不知道获取什么的平均成绩,于是我干脆通过输入课程表中的课程编号获取这门课的平均成绩。
然后的话如果要获取总平均成绩的话,就输入课程表中没有的ID(其实也可以增加一个特殊ID是这个特殊ID就平均成绩)这样的话每次新增课程,课程表中ID也会相应的增加,依旧可以获取对应的课程成绩。
判断的话我就直接拿他给我的id去查,查出来的list集合为空的话,那么就返回平均值。
@WebMethod(operationName = "GetCourseAverage")
public @WebResult(name = "CourseAverage")
CourseScore getAverage(
@WebParam(name = "CourseID") Integer courseId) {
CourseScore average = new CourseScore();
Integer vag = 0;
//查询对方想要什么科目,取出这门课的名字和课程字段
String hql = "from StudentCourse where id = ?1";
//集合为空说明没查到对应课程,返回平均值
List list1 = session.createQuery(hql)
.setParameter(1, courseId).list();
if (list1.size() == 0){
List<MVStudent> grade = getGrade();
for (MVStudent mvStudent : grade) {
vag = vag + mvStudent.getTotal() / grade.size();
}
average.setCourse("总平均分").setVag(vag);
return average;
}
StudentCourse course = (StudentCourse) list1.get(0);
System.out.println(course);
String sql = "select * from MV_STUDENT";
List<Object> list = session.createNativeQuery(sql).addScalar(course.getCourseName()).list();
System.out.println(list);
for (Object o : list) {
int i = Integer.parseInt(o.toString());
vag = vag + i / list.size();
}
average.setCourse(course.getName()).setVag(vag);
return average;
}
遇见问题:
这边遇见一个很尴尬的问题,主要感觉还是基础不牢(地动山摇啊)Hibernate 返回类型转Integer
4.获取某门课的成绩
这个也很简单,就是直接拿ID去物化视图里面查,然后取出自己想要的东西
@WebMethod(operationName = "GetStudentGrade")
public @WebResult(name = "StudentGrade")
AllCourses getCourseGrade(
@WebParam(name = "StudentID") Integer id) {
AllCourses courseGrade = new AllCourses();
String hql = "from MVStudent where id= ?1 ";
Query query = UtilSession.getQuery().createQuery(hql);
query.setParameter(1, id);
List<MVStudent> list = query.list();
MVStudent mvStudent = list.get(0);
courseGrade.setLanguage(mvStudent.getLanguage())
.setEnglish(mvStudent.getEnglish())
.setMathematics(mvStudent.getMathematics());
return courseGrade;
}
总结:
1.表设计其他想法
我一开始有另外一个想法,因为如果所有成绩放在一张表里面的话,一旦课程过多或者学生过多的话会导致成绩表特别的大和冗余,查询和遍历就很浪费时间。
于是我打算一门课程一张表。课程表里面拿一个字段出来存储这门课程所在的表(其实也不用),这样子每次增加新的课程的话新增一张表就好了。我总结下自认为的优缺点
优点:1、当数据量很大的时候查询速度快(比如大学里面大量数据)。2、插入方便,如果某个学生的某门课程需要更改(或者需要增加这门课程的分数),去对应的表里面修改就行了(而且还可以用多线程的方式并发插入,操作不同表的话,相对比多线程操作一张表要安全的多)。3、主要是我感觉这样子整个数据库的结构就清晰明了,不至于成绩表像锅大杂烩。
缺点:1、数据库表过多?(空间换时间?)2、操作繁琐(每个学生可能因为选的课不一样,导致查询繁琐——我觉得可以学生表里面可以使用一个字段,去存储自己选修了那些课解决这个问题,所以我觉得这不是问题)?3.其他问题欢迎大家指点一下,菜鸟阶段很多不懂。
2.杂谈随笔
写这个东西大概花了我两天半左右,需要使用hibernate,Oracle,WebService(学习)。
因为这些东西都是刚学的,而且就学了一下子,了解的比较浅,所以最大的问题反而是依赖的选择(因为我有时候根本不知道有这个依赖),环境的搭建(有时候明明照着网上操作,总会出莫名其妙的问题),因为技术比较老,网上资料都是 1*年甚至0*年的,很多东西都淘汰了。
1. 比如HIbernate和Oracle搭建,数据库驱动一开始需要去下载,后面想了想,公司Maven仓库里面肯定有配套的。下下来发现驱动好像HIbernate版本出问题,又去改了一下,搞了半天。然后很多配置也是0几年的,去网上找,很多配置都淘汰了,后面又找到官网去了
2.反正配置方面乱七八糟的问题(我想这可能就是架构师为啥工资这么高的问题,哈哈)每次一个项目从0到有最难的就是项目配置搭建
写代码业务其实就花了半天左右,这半天里面有一小半时间还是在解决上面说到的两个问题。感觉业务挺简单的。