![软件中间件技术UNIT14_第1页](http://file3.renrendoc.com/fileroot_temp3/2021-12/23/44c284b2-a2be-405e-8b60-562dd3b79360/44c284b2-a2be-405e-8b60-562dd3b793601.gif)
![软件中间件技术UNIT14_第2页](http://file3.renrendoc.com/fileroot_temp3/2021-12/23/44c284b2-a2be-405e-8b60-562dd3b79360/44c284b2-a2be-405e-8b60-562dd3b793602.gif)
![软件中间件技术UNIT14_第3页](http://file3.renrendoc.com/fileroot_temp3/2021-12/23/44c284b2-a2be-405e-8b60-562dd3b79360/44c284b2-a2be-405e-8b60-562dd3b793603.gif)
![软件中间件技术UNIT14_第4页](http://file3.renrendoc.com/fileroot_temp3/2021-12/23/44c284b2-a2be-405e-8b60-562dd3b79360/44c284b2-a2be-405e-8b60-562dd3b793604.gif)
![软件中间件技术UNIT14_第5页](http://file3.renrendoc.com/fileroot_temp3/2021-12/23/44c284b2-a2be-405e-8b60-562dd3b79360/44c284b2-a2be-405e-8b60-562dd3b793605.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、14.1 数据库表关系数据库表关系 1. 关系基数 2. CMP和BMP的不同点 3. 用BMP实现一对一关系 4.用BMP实现一对多关系 5. 用BMP实现多对多关系 6.用CMP实现例子 7. 用CMP实现一对一关系 8.用CMP实现一对多关系 9. 用CMP实现多对多关系1. 关系基数 关系基数有三种形式: 1:1关系 1:N关系 M:N关系CMP(Container-Managed Persistence)和CMR(Container-Managed Relationships) 2. CMP和BMP的不同点 BMP需要自己在Bean的编码中明确实现关系。 CMP是通过在部署描述文件中
2、声明Bean之间的关系,容器根据“声明”,自动在子类中生成处理关系的代码。3. 用BMP实现一对一关系 OrderPKOrderNameShipment12345Software Order10101ShipmentPKCityZipCode10101Austin78727Order:Shipment:3. 用BMP实现一对一关系 public class OrderBean implements EntityBean private String orderPK;private String orderName;private Shipment shipment;public Shipmen
3、t getShipment() return shipment;public void setShipment(Shipment s) this.shipment=s;3. 用BMP实现一对一关系 public void ejbLoad() /1:SQL SELECT Order/取出Order表中的Shipment/2.使用JNDI API查找ShipmentHome对象/3.调用ShipmentHome.findByPrimaryKey();/传入的参数是Order表中的Shipment,从而得到Shipment的EJB对象3. 用BMP实现一对一关系 public void ejbSto
4、re() /1.调用Shipment.getPrimaryKey(),/取出当前Shipment对象的主键类/2:SQL UPDATE Order4. 用BMP实现一对多关系 CompanyPKName12345The Middleware CompanyEmployeePKNameSexCompany20202EdM1234520203FloydM12345Company:Employee:4. 用BMP实现一对多关系 public class CompanyBean implements EntityBean private String companyPK;private String
5、companyName;private Vector employees;public Collection getEmployees() return employees;public void setEmployees(Collection e) this.employees=e;4. 用BMP实现一对多关系 public void ejbLoad() /1:SQL SELECT Company/2.使用JNDI API查找EmployeeHome对象/3.调用EmployeeHome.findByCompany(companyPK);public void ejbStore() /1:S
6、QL UPDATE Company5. 用BMP实现多对多关系 StudentPKNameCourse10101Joe Student2020210101Joe Student2020310102Smith Student2020210102Smith Student20203Student:5. 用BMP实现多对多关系 CoursePKNameStudent20202EJB for Architects1010120202EJB for Architects1010220203JSP and Servlets1010120203JSP and Servlets10102Course:5. 用
7、BMP实现多对多关系 public class StudentBean implements EntityBean private String studentPK;private String name;private Vector courses;public Collection getCourses() return courses;public void setCourses(Collection e) this.courses=e;.public void ejbLoad() /1:SQL SELECT Student/2.使用JNDI API查找CourseHome对象/3.调用
8、CourseHome.findByStudent(studentPK);public void ejbStore() /1:SQL UPDATE Student5. 用BMP实现多对多关系 public class CourseBean implements EntityBean private String coursePK;private String name;private Vector students;public Collection getStudents() return students;public void setStudents(Collection e) this.
9、students=e;.public void ejbLoad() /1:SQL SELECT Course/2.使用JNDI API查找StudentHome对象/3.调用StudentHome.findByCourse(coursePK);public void ejbStore() /1:SQL UPDATE Course6. 用CMP实现例子 基于在线内容管理系统的假想认证子系统的示例,它具有以下这些功能: 将用户登录到系统 认证用户处于某种角色 允许将用户组织成组,从而允许进行组操作 存储诸如地址和联系信息这样的用户信息 管理角色、用户和组 6. 用CMP实现例子 图图 1 1:Us
10、erUser、GroupGroup、Role Role 和和 UserInfo UserInfo 实体实体6. 用CMP实现例子系统中的实体的概述表明有四个截然不同的实体:User、Group、Role 和 UserInfo。这些实体有下面这三种关系: User 与 Role 相关联(多对多) 一个 User 有一条 UserInfo(一对一) 一个 Group 有多个 User(一对多)6. 用CMP实现例子表名:User字段名类型说明email(主键)VARCHAR(64)Email地址passwordVARCHAR(64)密码6. 用CMP实现例子表名:UserInfo字段名类型说明em
11、ail(主键)VARCHAREmail地址nameVARCHAR姓名deptVARCHAR组别workPhoneVARCHAR工作电话ageINTEGER年龄salaryINTEGER薪水6. 用CMP实现例子表名:Group字段名类型说明name(主键)VARCHAR名称description VARCHAR行业说明6. 用CMP实现例子表名:Role字段名类型说明name(主键)VARCHAR权限名称description VARCHAR权限描述7. 用CMP实现一对一关系一、设计实体一、设计实体Bean :UserInfoBean 1. 建立主体部分:建立主体部分: 在 CMP_Samp
12、le 项目中,右击“src” -新建 -Lomboz EJB Creation Wizard : 包(K) :javamxj.ejb.cmp 名称(M): UserInfo EJB Type: 选择 Container Managed Entity EJB 点击下一步。 Schema Name: UserInfoSchema Table Name: UserInfoTable 增加一个 email 栏: Field: email Field Type: java.lang.String Database Column: 电子邮件 SQL Type: varchar 并且使它成为主键并且使它成为
13、主键。7. 用CMP实现一对一关系 同样,再增加一个同样,再增加一个 name 栏:栏: Field: name Field Type: java.lang.String Database Column: 姓名姓名 SQL Type: varchar 同样,再增加一个同样,再增加一个 dept 栏:栏: Field: dept Field Type: java.lang.String Database Column: 组别组别 SQL Type: varchar 同样,再增加一个同样,再增加一个 workPhone栏:栏: Field: workPhone Field Type: java.l
14、ang.String Database Column: 工作电话工作电话 SQL Type: varchar 同样,再增加一个同样,再增加一个 age 栏:栏: Field: age Field Type: java.lang.Integer Database Column: 年龄年龄 SQL Type: Integer 同样,再增加一个同样,再增加一个 salary 栏:栏: Field: salary Field Type: java.lang.Integer Database Column: 薪水薪水 SQL Type: Integer 。7. 用CMP实现一对一关系 效果如下,最后点击
15、完成。效果如下,最后点击完成。7. 用CMP实现一对一关系7. 用CMP实现一对一关系 2. 2. 完成完成 ejbCreate ejbCreate 和和 ejbPostCreate ejbPostCreate 方法:方法: 7. 用CMP实现一对一关系 3. 3. 在类标记中加入以下标记。在类标记中加入以下标记。7. 用CMP实现一对一关系 4. ejb.select 4. ejb.select 方法方法 右击右击 UserInfoBean -J2EE UserInfoBean -J2EE -Add Select Method-Add Select Method 7. 用CMP实现一对一关系
16、 由于ejb.Home 方法需要通过 Home Home 接口才能间接使用,所以: 右击 UserInfoBean -J2EE -Add HomeHome Method 7. 用CMP实现一对一关系如图,点击“完成”后生成的代码,需要手动添加一条返回语句。 7. 用CMP实现一对一关系5.5. 将将 UserInfoBean UserInfoBean 加入到加入到 cmpEJB cmpEJB 模块模块中,然后中,然后 lomboz lomboz Generate EJB Generate EJB Classes Classes 如下图,如下图,会生成会生成6 6个文件,其中个文件,其中 Use
17、rInfoValue.java UserInfoValue.java 是是 “ejb.value-ejb.value-objectobject”标记生成的值标记生成的值对象。对象。 7. 用CMP实现一对一关系6 6. 添加一个抽象方法来调用值对象。 7. 用CMP实现一对一关系7 . 7 . 重 点 : 建 立重 点 : 建 立 UserInfoBean 与与 UserBean 的一对的一对一 的 双 向 关一 的 双 向 关系 :系 : 右 击右 击 UserInfoBean - J 2 E E - A d d CMR Relationship : 7. 用CMP实现一对一关系点击点击 完
18、成完成 后,生成如下代码(红框中的语句需要后,生成如下代码(红框中的语句需要手工添加):手工添加): 7. 用CMP实现一对一关系切换到切换到 UserBean.javaUserBean.java: 右击右击 UserBean UserBean -J2EE -Add -J2EE -Add CMR CMR Relationship : Relationship : 7. 用CMP实现一对一关系点击点击 完成完成 后,生成如下代码(红框中的语句需要后,生成如下代码(红框中的语句需要手工添加):手工添加): 7. 用CMP实现一对一关系点击点击“完成完成”后,生成如下代码(红框中的语句需要手后,生成
19、如下代码(红框中的语句需要手工添加):工添加):OkOk,再次,再次 Generate EJB ClassesGenerate EJB Classes。 7. 用CMP实现一对一关系 二、在二、在UserManagementBeanUserManagementBean中添加业务方法中添加业务方法1. 1. 增加一个增加一个 UserInfoLocalHome UserInfoLocalHome 的变量的变量 userHomeuserHome,并将,并将它放入它放入 ejbCreate ejbCreate 和和 ejbPassivate ejbPassivate 方法中。方法中。 7. 用CMP
20、实现一对一关系2. 2. 依次增加以下几个业务方法。依次增加以下几个业务方法。 7. 用CMP实现一对一关系7. 用CMP实现一对一关系7. 用CMP实现一对一关系7. 用CMP实现一对一关系7. 用CMP实现一对一关系 保存,保存,Generate EJB ClassesGenerate EJB Classes,启动,启动JBossJBoss服务器,服务器, Deploy ModuleDeploy Module。启动启动MySqlMySql,如果一切正常,则会在数据库中自动建立两个,如果一切正常,则会在数据库中自动建立两个表:表:usertable usertable 和和 userinfo
21、tableuserinfotable 。7. 用CMP实现一对一关系 三、创建客户端三、创建客户端1. 1. 右击右击 src src 文件夹文件夹 -新建新建 -Lomboz EJB Test Client -Lomboz EJB Test Client Wizard:Wizard: 7. 用CMP实现一对一关系2. 2. 修改生成的修改生成的 CMPClient2.javaCMPClient2.java,调用,调用UserManagementBeanUserManagementBean中的方法。先在中的方法。先在testBean()testBean()方法体外建立方法体外建立两个两个sta
22、ticstatic方法,简化代码调用。方法,简化代码调用。7. 用CMP实现一对一关系 / 创建用户创建用户 public static void createUsers(UserManagement userMgmt) throws RemoteException, CreateException System.out.println(向向UserInfo表中添加数据表中添加数据); userMgmt.addUser(, 密码密码1, 王一王一, Engineering, 111-1212, 22, 2000); userMgmt.addUser(, 密码密码2, 王二王二, Marketi
23、ng, 222-1213,40, 4000); userMgmt.addUser(zhangsanbbb.ccc, 密码密码3, 张三张三, IT, 1688888, 32, 2800); userMgmt.addUser(lisibbb.ccc, 密码密码4, 李四李四, Sales, 1288888, 28, 2700); userMgmt.addUser(zhubbb.eee, 密码密码5, 朱王五朱王五, Sales, 1588888, 38, 4500); userMgmt.addUser(, 分享分享Java快乐快乐, MXJ, IT, 1788888, 26, 2900); 7.
24、 用CMP实现一对一关系 / / 输出输出ArrayListArrayList private static void printList(ArrayList list) private static void printList(ArrayList list) Iterator i = list.iterator(); Iterator i = list.iterator(); while (i.hasNext() while (i.hasNext() Object details = (Object) i.next(); Object details = (Object) i.next()
25、; System.out.println(details.toString(); System.out.println(details.toString(); System.out.println(); System.out.println(); 7. 用CMP实现一对一关系 3. 3. 然后,将然后,将testBean()testBean()方法体内的方法体内的 myBean myBean 改成改成 userMgmtuserMgmt,再向表中添加数据及显示数据。再向表中添加数据及显示数据。public void testBean() public void testBean() try tr
26、y javamxj.ejb.cmp.UserManagement userMgmt = javamxj.ejb.cmp.UserManagement userMgmt = getHome().create(); getHome().create(); createUsers(userMgmt); createUsers(userMgmt); System.out.println( System.out.println(显示所有用户显示所有用户);); printList(userMgmt.getUsers(); printList(userMgmt.getUsers(); 7. 用CMP实现一
27、对一关系运行程序,则控制台输出如下:运行程序,则控制台输出如下: 7. 用CMP实现一对一关系4. 将将“createUsers(userMgmt);”语句注释掉,调用密码验证及语句注释掉,调用密码验证及改变用户姓名的方法。改变用户姓名的方法。 /createUsers(userMgmt); System.out.println(显示所有用户显示所有用户); printList(userMgmt.getUsers(); boolean login = false; login = userMgmt.verifyPassword(, 分享分享Java快乐快乐); System.out.print
28、ln(密码验证:密码验证: + login); System.out.println(改变用户改变用户的名字的名字); userMgmt.changeName(,Hello); 7. 用CMP实现一对一关系运行程序,则会发现运行程序,则会发现 javamxj javamxj 的姓名已被更改为的姓名已被更改为 HelloHello: 7. 用CMP实现一对一关系5. 5. 继续调用按年龄查询用户的方法:继续调用按年龄查询用户的方法: System.out.println(System.out.println(采用采用 ejb.finder ejb.finder 输出大于输出大于3030的用户的用
29、户);); printList(userMgmt.getUserByAge1(30); printList(userMgmt.getUserByAge1(30); System.out.println( System.out.println(采用采用 ejb.select ejb.select 输出年龄大输出年龄大于于3030的用户的用户);); printList(userMgmt.getUserByAge2(30); printList(userMgmt.getUserByAge2(30);7. 用CMP实现一对一关系输出:输出: 7. 用CMP实现一对一关系6.继续调用通过继续调用通过V
30、alueObject输出用户的方法:输出用户的方法: System.out.println(通过通过 ValueObject 输出用户信息:输出用户信息:); UserInfoValue users = userMgmt.getUsersInfo(); for (int index = 0; index -新建新建 - -Lomboz EJB Creation Wizard :Lomboz EJB Creation Wizard : 包(包(K K) :javamxj.ejb.cmpjavamxj.ejb.cmp 名称(名称(M M):): GroupGroup EJB Type EJB Ty
31、pe: 选择选择 Container Managed Entity EJBContainer Managed Entity EJB 点击下一步。点击下一步。 Schema Name: GroupSchemaSchema Name: GroupSchema Table Name: GroupTable Table Name: GroupTable 8. 用CMP实现一对多关系 增加一个增加一个 name name 栏:栏: Field: name Field: name Field Type: java.lang.String Field Type: java.lang.String Datab
32、ase Column: Database Column: 名称名称 SQL Type: varchar SQL Type: varchar 并且使它成为主键。并且使它成为主键。 同样,再增加一个同样,再增加一个 description description 栏:栏: Field: description Field: description Field Type: java.lang.String Field Type: java.lang.String Database Column: Database Column: 行业说明行业说明 SQL Type: varchar SQL Type
33、: varchar 8. 用CMP实现一对多关系效果如下,最后效果如下,最后点击完成。点击完成。8. 用CMP实现一对多关系2. 2. 完成完成 ejbCreate ejbCreate 和和 ejbPostCreate ejbPostCreate 方法:方法: 8. 用CMP实现一对多关系3. 3. 在类标记中加入以下标记:在类标记中加入以下标记: 8. 用CMP实现一对多关系4. 4. 增加一个增加一个Select methodSelect method和和Home methodHome method/ /* * * * Select method Select method* * ejb.
34、select ejb.select* * query = SELECT user.email FROM GroupSchema AS g, query = SELECT user.email FROM GroupSchema AS g, IN (g.users) AS user WHERE = ?1 IN (g.users) AS user WHERE = ?1 * * * */ /p u b l i c a b s t r a c t j a v a . u t i l . C o l l e c t i o n p u b l i c a b s t r a c
35、 t j a v a . u t i l . C o l l e c t i o n ejbSelectUserIDs(String groupName)ejbSelectUserIDs(String groupName) throws javax.ejb.FinderException; throws javax.ejb.FinderException; 8. 用CMP实现一对多关系4. 4. 增加一个增加一个Select methodSelect method和和Home methodHome method/ /* * * * Home method Home method* * thro
36、ws FinderException throws FinderException* * ejb.home-method view-type = local ejb.home-method view-type = local* */ /public java.util.ArrayList ejbHomeGetUserIDs(String public java.util.ArrayList ejbHomeGetUserIDs(String groupName)groupName) throws javax.ejb.FinderException throws javax.ejb.FinderE
37、xception r e t u r n ( j a v a . u t i l . A r r a y L i s t ) r e t u r n ( j a v a . u t i l . A r r a y L i s t ) ejbSelectUserIDs(groupName);ejbSelectUserIDs(groupName); 8. 用CMP实现一对多关系5. 5. 将将 GroupBean GroupBean 加入到加入到 cmpEJB cmpEJB 模块中,然后模块中,然后 lomboz -Generate EJB Classes lomboz -Generate EJB
38、 Classes 6. 6. 重点:建立重点:建立 GroupBean GroupBean 与与 UserBean UserBean 的一对多的双的一对多的双向联系:向联系: 右击右击 GroupBean -J2EE -Add CMR GroupBean -J2EE -Add CMR Relationship :Relationship : 8. 用CMP实现一对多关系8. 用CMP实现一对多关系 切换到切换到 UserInfoBean.javaUserInfoBean.java: 右击右击 UserInfoBean -J2EE -UserInfoBean -J2EE - Add CMR Re
39、lationship : Add CMR Relationship :8. 用CMP实现一对多关系8. 用CMP实现一对多关系 在在 UserInfoBean.java 中生成的代码(需要手工添加中生成的代码(需要手工添加jboss.relation):):/* Getter for CMR Relationship* erface-method view-type=local* ejb.relation name = GroupsHaveUserInfos* role-name = UserInfosInGroup* target-multiple = yes* jboss.r
40、elation * fk-column = 组别组别 * related-pk-field = name*/public abstract javamxj.ejb.cmp.GroupLocal getGroup();/* Setter for CMR Relationship* erface-method view-type=local*/public abstract void setGroup(javamxj.ejb.cmp.GroupLocal value); -Ok,再次,再次 Generate EJB Classes 8. 用CMP实现一对多关系 在在 UserInfo
41、Bean.java 中生成的代码(需要手工添加中生成的代码(需要手工添加jboss.relation):):/* Getter for CMR Relationship* erface-method view-type=local* ejb.relation name = GroupsHaveUserInfos* role-name = UserInfosInGroup* target-multiple = yes* jboss.relation * fk-column = 组别组别 * related-pk-field = name*/public abstract javam
42、xj.ejb.cmp.GroupLocal getGroup();/* Setter for CMR Relationship* erface-method view-type=local*/public abstract void setGroup(javamxj.ejb.cmp.GroupLocal value); -Ok,再次,再次 Generate EJB Classes 8. 用CMP实现一对多关系二、在二、在UserManagementBeanUserManagementBean中添加业务方法中添加业务方法增加一个增加一个 GroupLocalHome GroupLo
43、calHome 的变量的变量 groupHomegroupHome,并将它放入并将它放入 ejbCreate ejbCreate 和和 ejbPassivate ejbPassivate 方法中。方法中。1.1. 依次增加以下几个业务方法。依次增加以下几个业务方法。 8. 用CMP实现一对多关系 3.3.保存,保存,Generate EJB ClassesGenerate EJB Classes,启动,启动MySqlMySql, 启动启动JBossJBoss服务器,服务器, Deploy Module Deploy Module 。 一切正常,则会在一切正常,则会在 cmp_sample cm
44、p_sample 库中自动建立一个表:库中自动建立一个表:grouptablegrouptable。 8. 用CMP实现一对多关系 三、创建客户端三、创建客户端1. 1. 右击右击 src src 文件夹文件夹 -新建新建 -Lomboz EJB Test Client-Lomboz EJB Test Client Wizard: Wizard: 创建一个创建一个 CMPClient4.java CMPClient4.java 文件文件 2. 2. 修改生成的修改生成的 CMPClient4.javaCMPClient4.java,调用,调用UserManagementBeanUserMana
45、gementBean中的方法。中的方法。8. 用CMP实现一对多关系 3. 3. 运行客户端运行客户端控制台输出:控制台输出: 8. 用CMP实现一对多关系 数据库中相对应的表的改变:数据库中相对应的表的改变: 8. 用CMP实现一对多关系8. 用CMP实现一对多关系9. 用CMP实现多对多关系 public abstract class StudentBean implements EntityBean /成员变量在子类中实现,定义为abstract型 public abstract Collection getCourses(); public abstract void setCours
46、es (Collection courses); public void ejbLoad() /不需要任何代码 public void ejbStore() /不需要任何代码9. 用CMP实现多对多关系 public abstract class CourseBean implements EntityBean /成员变量在子类中实现,定义为abstract型 public abstract Collection getStudents(); public abstract void setStudents(Collection students); public void ejbLoad() /不需要任何代码 public void ejbStore() /不需要任何代码9. 用CMP实现多对多关系一、设计实体一、设计实体Bean Bean :RoleBeanRoleBean 1. 建立主体部分: 在 CMP_Sample 项目中,右击“src” -新建 -Lomboz EJB Creation
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度管沟施工环境保护与生态修复合同3篇
- 2025年度内墙刮瓷施工安全风险评估合同
- 二零二五年度苗木种植场地租赁及病虫害防治服务协议
- 2025至2030年工业用强固型便携式电脑项目投资价值分析报告
- 2025至2030年家用擦鞋器项目投资价值分析报告
- 2025至2030年变频器制动电阻项目投资价值分析报告
- 2025年鳗鱼康散项目可行性研究报告
- 2025年中国4孔8音市场调查研究报告
- 2025年电子体温计用温度传感器项目可行性研究报告
- 2025年中国PET塑料(啤酒)保鲜包装瓶市场调查研究报告
- 车间消防安全知识培训课件
- 劳动法概述劳动法与新经济业态的结合
- 华为经营管理-华为的研发管理(6版)
- 锂离子电池生产工艺流程图
- 平衡计分卡-化战略为行动
- 幼儿园小班下学期期末家长会PPT模板
- 矿山安全培训课件-地下矿山开采安全技术
- GB/T 6417.1-2005金属熔化焊接头缺欠分类及说明
- 2023年湖北成人学位英语考试真题及答案
- 《社会主义市场经济理论(第三版)》第七章社会主义市场经济规则论
- 《腰椎间盘突出》课件
评论
0/150
提交评论