2021SC@SDUSC
目录
HistoricalEntity.java和IDataProviderEntity.java
上一篇文章完成了openmeetings-db下的util目录中TimezoneUtil.java、RoomHelper.java、LocaleHelper.java、FormatHelper.java、DaoHelper.java和DtoHelper.java等文件的分析,那么本文首先结束util目录的源码分析,再进行下一步探索。
util目录
AuthLevelUtil.java
顾名思义,AuthLevel的意思是授权、认证的级别,这可能与用户或者房间对数据的访问权限等有关。首先看其引入的内容:
第一个引入的是OpenmeetingsVariables.getWebAppRootKey方法,已经分析过了,可以不再多说。紧接着,第二个和第三个引入的是HashSet和Set两个类,也是之前分析过的。再下面,引入的是同样在openmeetings-db模块下的entity目录中各类,由于还没有分析到,就暂且留个坑。最后两个也是之前分析过的与日志相关的类。这样看来,虽然引入的东西很多,但已经基本能够掌握其内容了。
接下来看类的定义:
第一行仍然是定义一个logger对象,此前已经分析过,不再多说。下面的构造器也可以省略不提。
接下来是一个public static方法,返回值类型为boolean,方法名为check;传入的形参有两个,一个是Set<User.Right> rights,一个是User.Right level。形参level的类型是User类的内部枚举类型Right,而Set的元素类型也是Right,是存储一系列权限等级的集合。
看方法体:
首先判断rights是否包含level,并将判断结果保存为boolean 变量result。然后,使用logger对象log输出信息,信息的格式里面体现了level的内容和rights中是否包含level。最终返回result。
继续看源码。方法hasUserLevel的形参也有一个相同的rights,方法体内调用check,判断rights中是否包含User.Right.Room,并返回,逻辑很简单。再下面的hasAdminLevel、hasGroupAdminLevel、hasWebServiceLevel和hasLoginLevel方法都是调用check方法,逻辑与hasUserLevel相同,不再多说。
着重分析一下getRoomRight方法。它的返回值类型为元素是Room的内部枚举类型Right的Set,形参列表为User u,Room r,Appointment a,int userCount。根据字面意思推测,它应该是确定User u在Room r中的权限。直接看方法体:
public static Set<Room.Right> getRoomRight(User u, Room r, Appointment a, int userCount) {
Set<Room.Right> result = new HashSet<>();
if (u == null) {
return result;
}
if (hasAdminLevel(u.getRights())) {
//admin user get superModerator level, no-one can kick him/her
result.add(Room.Right.superModerator);
} else if (r.isAppointment() && a != null && u.getId().equals(a.getOwner().getId())) {
// appointment owner is super moderator
result.add(Room.Right.superModerator);
}
if (result.isEmpty()) {
if (!r.isModerated() && 1 == userCount) {
//room is not moderated, first user is moderator!
result.add(Room.Right.moderator);
}
//performing loop here to set possible 'superModerator' right
for (RoomModerator rm : r.getModerators()) {
if (u.getId().equals(rm.getUser().getId())) {
result.add(rm.isSuperModerator() ? Room.Right.superModerator : Room.Right.moderator);
break;
}
}
//no need to loop if client is moderator
if (result.isEmpty() && r.getGroups() != null && !r.getGroups().isEmpty()) {
for (RoomGroup rg : r.getGroups()) {
for (GroupUser gu : u.getGroupUsers()) {
if (gu.getGroup().getId().equals(rg.getGroup().getId()) && gu.isModerator()) {
result.add(Room.Right.moderator);
break;
}
}
if (!result.isEmpty()) {
break;
}
}
}
}
if (Room.Type.conference == r.getType() && !result.contains(Room.Right.superModerator) && !result.contains(Room.Right.moderator) && !result.contains(Room.Right.video)) {
result.add(Room.Right.audio);
result.add(Room.Right.video);
}
return result;
}
首先初始化了一个Set,其元素是Room的内部类Right,名为result。接下来判断,如果u是个空的变量,则直接返回result。
然后进行判断,如果u有admin权限,则在result中添加一个元素,这个元素是Right枚举类型中包含的SuperModerator,也就是主持人(我认为,此处的含义正好就是,如果u是拥有管理员权限的,那么就在result中添加说明,证明他是主持人)。如果u没有admin权限,但是r调用isAppointMent方法的结果为真(具体含义留坑)、a非空(根据a的类型为AppointMent,猜测含义是a这个约定是存在的)、u的id等于a的拥有者的id,那么也可以在result中添加SuperModerator。执行完上面的操作后,相应地对result进行相似道理的一系列,例如先判断其是否为空,再根据r的类型进行继续的操作,最终返回result。所以,getRoomRight方法的作用与期初的猜测是相符的。
至此,AuthLevelUtil.java分析完毕,主要的作用是对用户和房间的权限进行读取。附上其源码:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.db.util;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
import java.util.HashSet;
import java.util.Set;
import org.apache.openmeetings.db.entity.calendar.Appointment;
import org.apache.openmeetings.db.entity.room.Room;
import org.apache.openmeetings.db.entity.room.RoomGroup;
import org.apache.openmeetings.db.entity.room.RoomModerator;
import org.apache.openmeetings.db.entity.user.GroupUser;
import org.apache.openmeetings.db.entity.user.User;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
public class AuthLevelUtil {
private static final Logger log = Red5LoggerFactory.getLogger(AuthLevelUtil.class, getWebAppRootKey());
private AuthLevelUtil() {}
public static boolean check(Set<User.Right> rights, User.Right level) {
boolean result = rights.contains(level);
log.debug(String.format("Level %s :: %s", level, result ? "[GRANTED]" : "[DENIED]"));
return result;
}
public static boolean hasUserLevel(Set<User.Right> rights) {
return check(rights, User.Right.Room);
}
public static Set<Room.Right> getRoomRight(User u, Room r, Appointment a, int userCount) {
Set<Room.Right> result = new HashSet<>();
if (u == null) {
return result;
}
if (hasAdminLevel(u.getRights())) {
//admin user get superModerator level, no-one can kick him/her
result.add(Room.Right.superModerator);
} else if (r.isAppointment() && a != null && u.getId().equals(a.getOwner().getId())) {
// appointment owner is super moderator
result.add(Room.Right.superModerator);
}
if (result.isEmpty()) {
if (!r.isModerated() && 1 == userCount) {
//room is not moderated, first user is moderator!
result.add(Room.Right.moderator);
}
//performing loop here to set possible 'superModerator' right
for (RoomModerator rm : r.getModerators()) {
if (u.getId().equals(rm.getUser().getId())) {
result.add(rm.isSuperModerator() ? Room.Right.superModerator : Room.Right.moderator);
break;
}
}
//no need to loop if client is moderator
if (result.isEmpty() && r.getGroups() != null && !r.getGroups().isEmpty()) {
for (RoomGroup rg : r.getGroups()) {
for (GroupUser gu : u.getGroupUsers()) {
if (gu.getGroup().getId().equals(rg.getGroup().getId()) && gu.isModerator()) {
result.add(Room.Right.moderator);
break;
}
}
if (!result.isEmpty()) {
break;
}
}
}
}
if (Room.Type.conference == r.getType() && !result.contains(Room.Right.superModerator) && !result.contains(Room.Right.moderator) && !result.contains(Room.Right.video)) {
result.add(Room.Right.audio);
result.add(Room.Right.video);
}
return result;
}
public static boolean hasAdminLevel(Set<User.Right> rights) {
return check(rights, User.Right.Admin);
}
public static boolean hasGroupAdminLevel(Set<User.Right> rights) {
return check(rights, User.Right.GroupAdmin);
}
public static boolean hasWebServiceLevel(Set<User.Right> rights) {
return check(rights, User.Right.Soap);
}
public static boolean hasLoginLevel(Set<User.Right> rights) {
return check(rights, User.Right.Login);
}
}
ApplicationHelper.java
接下来就是util目录下的最后一个java文件了。从名字可以看出,它的作用是对应用提供一些工具。老规矩,先来看引入的内容:
前3个引入的都是在之前的分析中经常看到的了。
接下来引入了一些springframework下的内容。关于sprinframework,简单作以下说明(参考自博文https://blog.csdn.net/qq_17768279/article/details/102610136):
Spring简介:
1、Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
2、Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
3、Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
4、Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
所以说,在此处导入spring相关的内容,是与代码模块的整体架构相关。如果展开细说,可能需要较大篇幅,因此这里就简单提一下,后面遇到再具体问题具体分析。
再下面,就引入了关于servelet的相关内容,为此简单介绍一下javax.servelet包(参考自博文Servlet 之 javax.servlet 包 - 随园 - 博客园和javax.servlet.ServletContext接口_weixin_33766168的博客-CSDN博客):
使用 Java 技术开发 WEB 应用程序 , 深入了解 Servlet 的机制对应用的开发将有重要的推动作用,而想深入了解 Servlet 的机制就不得不了解 javax.servlet 包。
javax.servlet 包中包含了 7 个接口 ,3 个类和 2 个异常类 , 它们分别是 :
1、接口:
RequestDispatcher,
Servlet,
ServletConfig,
ServletContext,
ServletRequest,
ServletResponse
SingleThreadModel
2、类:
GenericServlet,
ServletInputStream
ServletOutputStream
3、异常类:
ServletException
UnavailableException
4、Servlet 的生命周期:
在 Servlet 的接口中定义了一个 Servlet 的生命周期方法 , 分别是 Init,Service 和 Destroy。
在源码中引入了servlet.ServletContext接口,所以简要介绍一下:
<DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd>
一个servlet上下文是servlet引擎提供用来服务于Web应用的接口。Servlet上下文具有名字(它属于Web应用的名字)唯一映射到文件系统的一个目录。
一个servlet可以通过ServletConfig对象的getServletContext()方法得到servlet上下文的引用,如果servlet直接或间接调用子类GenericServlet,则可以使用getServletContext()方法。
Web应用中servlet可以使用servlet上下文得到:
1.在调用期间保存和检索属性的功能,并与其他servlet共享这些属性。
2.读取Web应用中文件内容和其他静态资源的功能。
3.互相发送请求的方式。
4.记录错误和信息化消息的功能。
ServletContext接口中的方法
1、Object getAttribute(String name) 返回servlet上下文中具有指定名字的对象,或使用已指定名捆绑一个对象。从Web应用的标准观点看,这样的对象是全局对象,因为它们可以被同一servlet在另一时刻访问。或上下文中任意其他servlet访问。
2、void setAttribute(String name,Object obj) 设置servlet上下文中具有指定名字的对象。
3、Enumeration getAttributeNames() 返回保存在servlet上下文中所有属性名字的枚举。
4、ServletContext getContext(String uripath) 返回映射到另一URL的servlet上下文。在同一服务器中URL必须是以“/”开头的绝对路径。
5、String getInitParameter(String name) 返回指定上下文范围的初始化参数值。此方法ServletConfig方法名称不一样,后者只应用于已编码的指定servlet。此方法应用于上下文中所有的参数。
6、Enumeration getInitParameterNames() 返回(可能为空)指定上下文范围的初始化参数值名字的枚举值。
7、int getMajorVersion() 返回此上下文中支持servlet API级别的最大和最小版本号。
8、int getMinorVersion()
9、String getMimeType(String fileName) 返回指定文件名的MIME类型。典型情况是基于文件扩展名,而不是文件本身的内容(它可以不必存在)。如果MIME类型未知,可以返回null。
10、RequestDispatcher getNameDispatcher(String name) 返回具有指定名字或路径的servlet或JSP的 RequestDispatcher。如果不能创建RequestDispatch,返回null。如果指定路径,必须心“/”开头,并且是相对于servlet上下文的顶部。
11、RequestDispatcher getNameDispatcher(String path)
12、String getRealPath(String path) 给定一个URI,返回文件系统中URI对应的绝对路径。如果不能进行映射,返回null。
13、URL getResource(String path) 返回相对于servlet上下文或读取URL的输入流的指定绝对路径相对应的URL,如果资源不存在则返回null。
14、InputStream getResourceAsStream(String path)
15、String getServerInfo() 返顺servlet引擎的名称和版本号。
16、void log(String message)
17、void log(String message,Throwable t) 将一个消息写入servlet注册,如果给出Throwable参数,则包含栈轨迹。
18、void removeAttribute(String name) 从servlet上下文中删除指定属性
然后引入了之前分析过的IApplication、IWebSession,以及db模块和util模块下的类。再下面就是引入了wicket包下的各种类了,为了提高效率,仅在代码中其出现时分析。
接下来看类的定义:
像往常一样,ApplicationHelper也有一个logger类型的private static final变量字段log,用作日志。然后,还有一个private static final变量字段SYNC_OBJ,但它的类型是Object,是所有类的最终父类。根据其名字中的sync,猜想它是与同步操作相关的变量,而根据它的类型是Object则可能是因为它可能需要根据不同的类相转换。
越过构造器,下面是3个看上去很像的方法,其中第一个和第三个是重载。
因此我们先看第二个方法_ensureApplication():
public static IApplication _ensureApplication() {
if (Application.exists()) {
return (IApplication)Application.get();
}
synchronized (SYNC_OBJ) {
if (Application.exists()) {
return (IApplication)Application.get();
}
WebApplication app = (WebApplication)Application.get(getWicketApplicationName());
LabelDao.initLanguageMap();
if (app == null) {
try {
app = (WebApplication)LabelDao.getAppClass().newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
log.error("Failed to create Application");
return null;
}
app.setServletContext(new MockServletContext(app, null));
app.setName(getWicketApplicationName());
ServletContext sc = app.getServletContext();
OMContextListener omcl = new OMContextListener();
omcl.contextInitialized(new ServletContextEvent(sc));
XmlWebApplicationContext xmlContext = new XmlWebApplicationContext();
xmlContext.setConfigLocation("classpath:applicationContext.xml");
xmlContext.setServletContext(sc);
xmlContext.refresh();
sc.setAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, xmlContext);
app.setConfigurationType(RuntimeConfigurationType.DEPLOYMENT);
ThreadContext.setApplication(app);
app.initApplication();
} else {
ThreadContext.setApplication(app);
}
return (IApplication)Application.get(getWicketApplicationName());
}
}
根据方法名,它的作用就是确定Application是否存在。因此,首先调用了Application.exists()进行判断,如果结果为true就直接返回Application.get()的强转为IApplication类型的结果。
在下面就出现了一个较为陌生的关键字,即synchronized。与操作系统中的同步互斥内容相类似,synchronized就是一个同步锁,关于它进行简单地介绍(参考自博文Java中synchronized详解 - songguojun - 博客园):
Synchronized同步方法可以支持使用一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。
简单就是说Synchronized的作用就是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保同一个时刻最多只有一个线程执行同步代码,从而保证多线程环境下并发安全的效果。 如果有一段代码被Synchronized所修饰,那么这段代码就会以原子的方式执行,当多个线程在执行这段代码的时候,它们是互斥的,不会相互干扰,不会同时执行。
Synchronized工作机制是在多线程环境中使用一把锁,在第一个线程去执行的时候去获取这把锁才能执行,一旦获取就独占这把锁直到执行完毕或者在一定条件下才会去释放这把锁,在这把锁释放之前其他的线程只能阻塞等待。
synchronized是Java中的关键字,被Java原生支持,是一种最基本的同步锁。
它修饰的对象有:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
在源码中应该修饰的是一个代码块,因此在同一时刻最多只有一个线程执行这段代码。
在同步代码块中的内容也是通过一系列的手段完成对Application的确认,虽然代码量很大但逻辑较简单,与之前分析过的源码相似,因此不再多说。
接下来看第三个方法ensureApplication(Long langId):
public static IApplication ensureApplication(Long langId) {
IApplication a = _ensureApplication();
if (ThreadContext.getRequestCycle() == null) {
ServletWebRequest req = new ServletWebRequest(new MockHttpServletRequest((Application)a, new MockHttpSession(a.getServletContext()), a.getServletContext()), "");
RequestCycleContext rctx = new RequestCycleContext(req, new MockWebResponse(), a.getRootRequestMapper(), a.getExceptionMapperProvider().get());
ThreadContext.setRequestCycle(new RequestCycle(rctx));
}
if (ThreadContext.getSession() == null) {
WebSession s = WebSession.get();
if (langId > 0) {
((IWebSession)s).setLanguage(langId);
}
}
return a;
}
其返回值是IApplication类型,传入的形参是Long langId。仔细分析源码发现,这里的源码与线程上下文相关结合起来,根据调用_ensureApplication方法返回的对象,进行一系列操作,最终将该对象返回。也就是说,其逻辑是确认并新建了Application的存在之后,对它进行http、web等方面的检测与加工,最终返回一个结果。上面重载它的方法原理相同,不再多说。
再下面的方法是destroyApplication(),直接看其方法体:
这里体现了Application的生命周期,即通过ensure创建Application,最终通过destroy销毁Application。因此在destroyApplication中,最终通过线程上下文设置了application、requestCycle、session均为null,结束了Application的生命周期。
至此,ApplicationHelper.java分析完毕。它的作用是对整体的应用生命周期进行把控,包括创建和销毁application的存在。附上其源码:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.db.util;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWicketApplicationName;
import static org.red5.logging.Red5LoggerFactory.getLogger;
import static org.springframework.web.context.WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
import static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import org.apache.openmeetings.IApplication;
import org.apache.openmeetings.IWebSession;
import org.apache.openmeetings.db.dao.label.LabelDao;
import org.apache.openmeetings.util.OMContextListener;
import org.apache.wicket.Application;
import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.ThreadContext;
import org.apache.wicket.mock.MockWebResponse;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
import org.apache.wicket.protocol.http.mock.MockHttpSession;
import org.apache.wicket.protocol.http.mock.MockServletContext;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.cycle.RequestCycleContext;
import org.slf4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
public class ApplicationHelper {
private static final Logger log = getLogger(ApplicationHelper.class, getWebAppRootKey());
private static final Object SYNC_OBJ = new Object();
private ApplicationHelper() {}
public static IApplication ensureApplication() {
return ensureApplication(-1L);
}
public static IApplication _ensureApplication() {
if (Application.exists()) {
return (IApplication)Application.get();
}
synchronized (SYNC_OBJ) {
if (Application.exists()) {
return (IApplication)Application.get();
}
WebApplication app = (WebApplication)Application.get(getWicketApplicationName());
LabelDao.initLanguageMap();
if (app == null) {
try {
app = (WebApplication)LabelDao.getAppClass().newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
log.error("Failed to create Application");
return null;
}
app.setServletContext(new MockServletContext(app, null));
app.setName(getWicketApplicationName());
ServletContext sc = app.getServletContext();
OMContextListener omcl = new OMContextListener();
omcl.contextInitialized(new ServletContextEvent(sc));
XmlWebApplicationContext xmlContext = new XmlWebApplicationContext();
xmlContext.setConfigLocation("classpath:applicationContext.xml");
xmlContext.setServletContext(sc);
xmlContext.refresh();
sc.setAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, xmlContext);
app.setConfigurationType(RuntimeConfigurationType.DEPLOYMENT);
ThreadContext.setApplication(app);
app.initApplication();
} else {
ThreadContext.setApplication(app);
}
return (IApplication)Application.get(getWicketApplicationName());
}
}
public static IApplication ensureApplication(Long langId) {
IApplication a = _ensureApplication();
if (ThreadContext.getRequestCycle() == null) {
ServletWebRequest req = new ServletWebRequest(new MockHttpServletRequest((Application)a, new MockHttpSession(a.getServletContext()), a.getServletContext()), "");
RequestCycleContext rctx = new RequestCycleContext(req, new MockWebResponse(), a.getRootRequestMapper(), a.getExceptionMapperProvider().get());
ThreadContext.setRequestCycle(new RequestCycle(rctx));
}
if (ThreadContext.getSession() == null) {
WebSession s = WebSession.get();
if (langId > 0) {
((IWebSession)s).setLanguage(langId);
}
}
return a;
}
public static void destroyApplication() {
WebApplication app = (WebApplication)Application.get(getWicketApplicationName());
WebApplicationContext ctx = getWebApplicationContext(app.getServletContext());
app.internalDestroy(); //need to be called to
if (ctx != null) {
((XmlWebApplicationContext)ctx).close();
}
ThreadContext.setApplication(null);
ThreadContext.setRequestCycle(null);
ThreadContext.setSession(null);
}
public static WebApplicationContext getApplicationContext(Long langId) {
return getWebApplicationContext(((WebApplication)ensureApplication(langId)).getServletContext());
}
}
到这里,db模块下的util目录也已经分析结束。接下来,在此前分析过程中遇到的类大量来自entity目录,因此下面开始对entity目录的源码分析。
entity目录
entity意为实体,也就是现实世界中存在的各种角色,例如用户、管理员等等。entity目录肯定对应于数据库中的表,因此,在entity目录下,存在user、room、log等子目录,对应于现实世界中相应的角色。这个目录也会对数据库进行一些操作。其结构如下:
在entity目录中,除了子目录,还存在两个文件:HistoricalEntity.java和IDataProviderEntity.java。由于它们比较简单,首先分析一下这两个文件。
HistoricalEntity.java和IDataProviderEntity.java
IDataProviderEntity.java
整个文件定义了一个接口,直接看源码:
导入的是可序列化类Serializable。定义了IDataProviderEntity接口,继承自Serializable,说明实现了IDataProviderEntity接口的类也是可序列化的。接口内部只定义了id的getter和setter,比较简单,不再多说。
HistoricalEntity.java
直接看其源码中导入的类:
首先导入的是Date类,已经见过多次,不再解释。下面导入了javax.persistence.Column和javax.persistence.MappedSuperclass,需要简单解释一下(参考自博文javax.persistence的@column - 24601 - 博客园和虎小五进阶之路——@MappedSuperclass注解的使用_虎小五进阶之路-CSDN博客):
首先看@Column。
@Column表示数据库映射实体对应的列,有很多属性(写在括号里面的,用逗号隔开的)
name属性表示对应的列名
columnDefinition属性表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用,如果数据库中表已经建好,该属性没有必要使用:
@Column(name = "isLock", columnDefinition = "bit(1) COMMENT '是否锁定 0未锁1锁定.'")
private Boolean isLock = false;
这等价于:
CREATE TABLE tb_emp1
(
is_lock bit(1) COMMENT '是否锁定 0未锁1锁定.'
);
下面为一些可用的属性
1、name
name属性定义了被标注字段在数据库表中所对应字段的名称;
2、unique
unique属性表示该字段是否为唯一标识,默认为false。如果表中有一个字段需要唯一标识,则既可以使用该标记,也可以使用@Table标记中的@UniqueConstraint。
3、nullable
nullable属性表示该字段是否可以为null值,默认为true。
4、insertable
insertable属性表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
5、updatable
updatable属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
6、columnDefinition
columnDefinition属性表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。(也就是说,如果DB中表已经建好,该属性没有必要使用。)
7、table
table属性定义了包含当前字段的表名。
8、length
length属性表示字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。
9、precision和scale
precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。
也就是说,@Column的作用就是定义数据库中对应的列。
再看@MappedSuperclass。
当我们进行开发项目时,我们经常会用到实体映射到数据库表的操作,此时我们经常会发现在我们需要隐射的几个实体类中,有几个共同的属性,例如编号ID,创建者,创建时间,修改者,修改时间,备注等。遇到这种情况,我们可能会想到把这些属性抽象出来当成一个父类,然后再以不同的实体类来继承这个父类。
那么,我们便可以使用@MappedSuperclass注解,通过这个注解,我们可以将该实体类当成基类实体,它不会隐射到数据库表,但继承它的子类实体在隐射时会自动扫描该基类实体的隐射属性,添加到子类实体的对应数据库表中。
使用环境:
1.@MappedSuperclass注解使用在父类上面,是用来标识父类的
2.@MappedSuperclass标识的类表示其不能映射到数据库表,因为其不是一个完整的实体类,但是它所拥有的属性能够隐射在其子类对用的数据库表中
3.@MappedSuperclass标识得类不能再有@Entity或@Table注解
最后导入的是org.simpleframework.xml.Element,个人理解,它是简化在java源代码中进行xml文件的序列化与反序列化操作的。接下来看类的定义:
首先使用了@MappedSuperclass标识基类,也就是抽象类HistoricalEntity。在类体里面,HistoricalEntity类实现了刚才说过的IDataProviderEntity接口,这表示它是可序列化的,并且获得了id的getter和setter。由于它也是抽象类,因此没有实现id的getter和setter,等待子类实现。
然后定义了private static final的字段serialVersionUID,数据类型为long,大小为1L。它的意思应该是序列化版本的标记,且固定为1L,是为了统一数据存储的标识。接下来就比较简单了,定义了若干的列并将其进行xml序列化,还有下面的一些getter和setter。
附上源码:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.db.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import org.simpleframework.xml.Element;
@MappedSuperclass
public abstract class HistoricalEntity implements IDataProviderEntity {
private static final long serialVersionUID = 1L;
@Column(name = "inserted")
@Element(name = "created", data = true, required = false)
private Date inserted;
@Column(name = "updated")
@Element(name = "updated", data = true, required = false)
private Date updated;
@Column(name = "deleted", nullable = false)
@Element(data = true, required = false)
private boolean deleted;
public Date getInserted() {
return inserted;
}
public void setInserted(Date inserted) {
this.inserted = inserted;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
javax.persistence
在entity目录中,多处出现了javax.persistence。因此,为了更好地研究源码,不得不先将javax.persistence的内容分析清楚。
首先,其官方描述为:
意为,javax.persistence 包包含定义持久性提供者和托管类以及 Java Persistence API 客户端之间的契约的类和接口。 接下来,对其语法进行说明(参考自博文https://www.iteye.com/blog/67566894-659829):
举例:
@SuppressWarnings("serial")
@Entity
@Table(name="T_X")
public class X implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(length=32)
private String name;
@Transient //表示此数据不在数据库表里建立属性
private String temp;
@Temporal(TemporalType.TIMESTAMP) //这个是带时分秒的类型
private Date date;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "x")
private A a;
}
@SuppressWarnings("serial")
@Entity
@Table(name="T_A")
public class A implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "a", fetch = FetchType.EAGER)
private List<B> b = new ArrayList<B>();
@OneToOne()
@JoinColumn(name = "x_Id") //加这句后就会双方共同维护关系
private X x;
}
@SuppressWarnings("serial")
@Entity
public class B implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected int id;
@ManyToOne()
@JoinColumn(name = "a_id")
protected A a;
}
要注意的是:fetch = FetchType.EAGER这句话在一个类里面只能出现一次,出现两次就会报错“cannot simultaneously fetch multiple bags”,要把其他的改为fetch = FetchType.LAZY延迟加载就可以了。听说把List集合改为Set也能解决这个错误。
其他要点:
1、@Table(name="T_X")这句话可以不写,不写就以类名作为表名
2、如果想让两个类的属性生成一个数据表,在一个类里这样加入另一个类即可: @Embedded
private C c;
3、如果想要一个类继承另一个类的所有属性,则在父类里这样写:
@SuppressWarnings("serial")
@Entity
@MappedSuperclass //增加这一行
并把父类的所有属性的private改为protected即可
4、建议在一对多关联中在"一"方用延迟加载"多"方可以在HQL中显式的"迫切左外连接" left join fetch 这样做Hibernate可以少访问数据库,也可以用"@BatchSize(size = 5)"来减少访问数据库的次数
关于@的说明:
1. @Id 声明属性为主键
2. @GeneratedValue表示主键是自动生成策略,一般该注释和 @Id 一起使用
3. @Entity 任何 hibernte 映射对象都要有次注释
4. @Table(name = “tablename”) 类声明此对象映射到哪个表
5. @Column(name = “Name”,nullable=false,length=32) 声明数据 库字段和类属性对应关系
6. @Lob 声明字段为 Clob 或 Blob 类型
7. @OneToMany(mappedBy=”order”,cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@OrderBy(value = “id ASC”)
一对多声明,和 ORM 产品声明类似,一看就明白了。
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumn(name = “order_id”)
声明为双向关联
8. @Temporal(value=TemporalType.DATE) 做日期类型转换。
9. @OneToOne(optional= true,cascade = CascadeType.ALL, mappedBy = “person”)
一对一关联声明
@OneToOne(optional = false, cascade = CascadeType.REFRESH)
@JoinColumn(name = “Person_ID”, referencedColumnName = “personid”,unique = true)
声明为双向关联
10. @ManyToMany(mappedBy= “students”)
多对多关联声明。
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinTable(name = “Teacher_Student”,
joinColumns = {@JoinColumn(name = “Teacher_ID”, referencedColumnName = “teacherid”)},
inverseJoinColumns = {@JoinColumn(name = “Student_ID”, referencedColumnName =
“studentid”)})
多对多关联一般都有个关联表,是这样声明的!
11. @Transiten表示此属性与表没有映射关系,是一个暂时的属性
12. @Cache(usage= CacheConcurrencyStrategy.READ_WRITE)表示此对象应用缓存
以上应该包含了源码中可能出现的注解了,如果还有漏掉的再自行查找就好了。
entity/user目录
在entity目录下,包含许多子目录,每个文件夹里包含了不同的实体,接下来从user子目录开始分析。user目录下面包含了以下的java文件:
依次粗略查看其源码,发现:
User.java:
UserContact.java:
Address.java:
容易发现,其代码结构都类似,均是与数据库的表相关。因此,我们只分析User.java,其他源码类比理解即可。
接下来开始分析User.java。
文件引入的内容,前一些其中很多都已经见过,例如:
后面有一些相对陌生,例如:
前面的ElementDependent、FetchAttribute、FetchGroup、FetchGroups 、LoadFetchGroup也还没有见过。后面的ForeignKey虽然也没有遇到过,但它代表的是数据库属性中的外码约束,因此不难理解。所以,接下来要说清楚一些没有见过的内容的意义。
1、ElementDependent
意思是,依赖数组、集合或地图元素标记。
2、FetchGroup
由于资料太少,只能勉强猜测大意是定义的一些需要获取的组的定义。
3、FetchAttribute
意思是,在FetchGroup中包含的永久性属性。在前面的FetchGroup定义中也看到了FetchAttribute的身影。
4、FetchGroups自然就是FerchGroup的集合了。
5、NamedQuery和NamedQueries(参考自博文JPA的@NamedQuery注解_xiaoQL520的博客-CSDN博客_@namedquery)
1)使用@NamedQuery注解在实体类中定义命名查询
@NamedQuery(name="findAllUser",query="SELECT c FROM Customer c")
其中name
指定命名查询的名称,query
指定命名查询的语句
其中name
指定命名查询的名称,query
属性指定命令查询的语句。
2)使用@NamedQueries定义多个命名查询
@NamedQueries({
@NamedQuery(name="findAllCustomer",query="SELECT c FROM Customer"),
@NamedQuery(name="findCoustomerWithId",query="SELECT c FROM Customer c WHERE c.id=?1")
@NamedQuery(name="findCustomerWithName",query="SELECT c FROM Customer c WHERE c.name = :name"
})
如果用到?
的形式获取参数。那么它是按照方法的参数顺序来取值的;也可以使用:
匹配参数
3)定义好命名查询后,可以使用EntityManager的createNamedQuery方法传入命名查询的名称创建查询
createNamedQuery("findAllCustomer");
4)使用方法
A、修改实体
在@Entity下增加@NamedQuery的定义(sql表达式里面的表名要和当前的Entity一致)
如果@Entity注解中使用了name属性,例如:@Entity(name = "my_customer"), 则${entityName} 对应的就是 my_customer 表。
B、修改CustomerRepository
增加方法 Customer findCustomerWithName(String name);
C、在CustomerController中调用
/**
* 查询FirstName为指定用户昵称
*/
@RequestMapping("/findCustomerWithName")
public void findCustomerWithName(){
Customer customer = repository.findCustomerWithName("John");
if(customer!=null){
System.out.println(customer.toString());
}
}
简单来说,NamedQuery就是封装一些sql语句,并且使之能够通过方法直接调用。
解释了这些之后,可以正式开始分析了。
首先就是定义了FetchGroups:
其中包含了两个FetchGroup,其name分别是backupexport和groupUsers。
再下面,就定义了一系列查询语句:
因为有学过数据库的基础,因此这些语句理解起来也并不困难。
再下面:
@Table定义了表的名称,@Root说明解析为xml时的根标签为<user></user>
再下面就是类的定义了:
首先说明了User是继承自HistoricalEntity的。刚刚提到,HistoricalEntity是一个抽象类,实现了IDataProviderEntity接口。那么,User是可序列化的,并且带有HistoricalEntity和IDataProviderEntity定义的字段和方法。
在User内部也定义了若干字段,包括了之前也见过的serialVersionUID,同样也是1L。然后是5个public static final int变量,也都代表了id,但是具体含义单在这里很难看出,先继续看源码。
接下来是通过@XmlType定义了下面的枚举类型Right的名称空间,也就是在实际java文件中的位置。再下面是类似的几个定义,不再多说。
再往下,就是定义数据库表的各列属性了,基本思路都分析过了,也不再多说。文件的最后,是包括getter、setter以及一些比较容易分析的方法在内的代码,虽然比较多,但逻辑都很简单,不再多说。
至此,User.java分析完毕。在这个文件中,通过java代码实现了User实体,并能够映射到数据库的对应表中。分析到这里,我才逐渐明白了db模块的含义,因为它的本质是对数据库进行操作。
附上源码:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.db.entity.user;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSipContext;
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSipEnabled;
import static org.apache.wicket.util.string.Strings.escapeMarkup;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlType;
import org.apache.openjpa.persistence.ElementDependent;
import org.apache.openjpa.persistence.FetchAttribute;
import org.apache.openjpa.persistence.FetchGroup;
import org.apache.openjpa.persistence.FetchGroups;
import org.apache.openjpa.persistence.LoadFetchGroup;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
import org.apache.openmeetings.db.dao.label.LabelDao;
import org.apache.openmeetings.db.entity.HistoricalEntity;
import org.apache.openmeetings.db.entity.label.OmLanguage;
import org.apache.openmeetings.db.entity.server.Sessiondata;
import org.apache.openmeetings.util.crypt.CryptProvider;
import org.apache.openmeetings.util.crypt.MD5;
import org.apache.wicket.util.string.Strings;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
/**
* Entity to store user data, password field is {@link FetchType#LAZY}, so that
* is why there is an extra udpate statement at this moment
*
* @author sebawagner, solomax
*
*/
@Entity
@FetchGroups({
@FetchGroup(name = "backupexport", attributes = { @FetchAttribute(name = "password") })
, @FetchGroup(name = "groupUsers", attributes = { @FetchAttribute(name = "groupUsers")})
})
@NamedQueries({
@NamedQuery(name = "getUserById", query = "SELECT u FROM User u WHERE u.id = :id"),
@NamedQuery(name = "getUsersByIds", query = "select c from User c where c.id IN :ids"),
@NamedQuery(name = "getUserByLogin", query = "SELECT u FROM User u WHERE u.deleted = false AND u.type = :type AND u.login = :login AND ((:domainId = 0 AND u.domainId IS NULL) OR (:domainId > 0 AND u.domainId = :domainId))"),
@NamedQuery(name = "getUserByEmail", query = "SELECT u FROM User u WHERE u.deleted = false AND u.type = :type AND u.address.email = :email AND ((:domainId = 0 AND u.domainId IS NULL) OR (:domainId > 0 AND u.domainId = :domainId))"),
@NamedQuery(name = "getUserByHash", query = "SELECT u FROM User u WHERE u.deleted = false AND u.type = :type AND u.resethash = :resethash"),
@NamedQuery(name = "getUserByExpiredHash", query = "SELECT u FROM User u WHERE u.resetDate < :date"),
@NamedQuery(name = "getContactByEmailAndUser", query = "SELECT u FROM User u WHERE u.deleted = false AND u.address.email = :email AND u.type = :type AND u.ownerId = :ownerId"),
@NamedQuery(name = "selectMaxFromUsersWithSearch", query = "select count(c.id) from User c "
+ "where c.deleted = false " + "AND ("
+ "lower(c.login) LIKE :search "
+ "OR lower(c.firstname) LIKE :search "
+ "OR lower(c.lastname) LIKE :search )"),
@NamedQuery(name = "getAllUsers", query = "SELECT u FROM User u ORDER BY u.id"),
@NamedQuery(name = "getPassword", query = "SELECT u.password FROM User u WHERE u.deleted = false AND u.id = :userId "),
@NamedQuery(name = "updatePassword", query = "UPDATE User u SET u.password = :password WHERE u.id = :userId"), //
@NamedQuery(name = "getNondeletedUsers", query = "SELECT u FROM User u WHERE u.deleted = false"),
@NamedQuery(name = "countNondeletedUsers", query = "SELECT COUNT(u) FROM User u WHERE u.deleted = false"),
@NamedQuery(name = "getUsersByGroupId", query = "SELECT u FROM User u WHERE u.deleted = false AND u.groupUsers.group.id = :groupId"),
@NamedQuery(name = "getExternalUser", query = "SELECT u FROM User u WHERE u.deleted = false AND u.externalId LIKE :externalId AND u.externalType LIKE :externalType"),
@NamedQuery(name = "getUserByLoginOrEmail", query = "SELECT u from User u WHERE u.deleted = false AND u.type = :type AND (u.login = :userOrEmail OR u.address.email = :userOrEmail)")
})
@Table(name = "om_user")
@Root(name = "user")
public class User extends HistoricalEntity {
private static final long serialVersionUID = 1L;
public static final int SALUTATION_MR_ID = 1;
public static final int SALUTATION_MS_ID = 2;
public static final int SALUTATION_MRS_ID = 3;
public static final int SALUTATION_DR_ID = 4;
public static final int SALUTATION_PROF_ID = 5;
@XmlType(namespace="org.apache.openmeetings.user.right")
public enum Right {
Admin // access to Admin module
, GroupAdmin // partial access to Admin module (should not be directly assigned)
, Room // enter the room
, Dashboard // access the dashboard
, Login // login to Om internal DB
, Soap // use rest/soap calls
}
@XmlType(namespace="org.apache.openmeetings.user.type")
public enum Type {
user
, ldap
, oauth
, external
, contact
}
@XmlType(namespace="org.apache.openmeetings.user.salutation")
public enum Salutation {
mr(SALUTATION_MR_ID)
, ms(SALUTATION_MS_ID)
, mrs(SALUTATION_MRS_ID)
, dr(SALUTATION_DR_ID)
, prof(SALUTATION_PROF_ID);
private int id;
Salutation(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static Salutation get(Long type) {
return get(type == null ? 1 : type.intValue());
}
public static Salutation get(Integer type) {
return get(type == null ? 1 : type.intValue());
}
public static Salutation get(int type) {
Salutation rt = Salutation.mr;
switch (type) {
case SALUTATION_MS_ID:
rt = Salutation.ms;
break;
case SALUTATION_MRS_ID:
rt = Salutation.mrs;
break;
case SALUTATION_DR_ID:
rt = Salutation.dr;
break;
case SALUTATION_PROF_ID:
rt = Salutation.prof;
break;
default:
//no-op
}
return rt;
}
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@Element(data = true, name = "user_id")
private Long id;
@Column(name = "age")
@Element(data = true, required = false)
private Date age;
@Column(name = "firstname")
@Element(data = true, required = false)
private String firstname;
@Column(name = "lastlogin")
private Date lastlogin;
@Column(name = "lastname")
@Element(data = true, required = false)
private String lastname;
@Column(name = "displayName")
@Element(data = true, required = false)
private String displayName;
@Column(name = "login")
@Element(data = true, required = false)
private String login;
@Basic(fetch = FetchType.LAZY)
@Column(name = "password", length = 1024)
@LoadFetchGroup("backupexport")
@Element(name = "pass", data = true, required = false)
private String password;
@Column(name = "regdate")
@Element(data = true, required = false)
private Date regdate;
@Column(name = "salutation")
@Enumerated(EnumType.STRING)
@Element(name = "title_id", data = true, required = false)
private Salutation salutation;
@Column(name = "pictureuri")
@Element(data = true, required = false)
private String pictureUri;
@Column(name = "language_id")
@Element(name = "language_id", data = true, required = false)
private long languageId;
@Column(name = "resethash")
@Element(data = true, required = false)
private String resethash;
@Column(name = "resetdate")
@Element(data = true, required = false)
private Date resetDate;
@Column(name = "activatehash")
@Element(data = true, required = false)
private String activatehash;
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "address_id", insertable = true, updatable = true)
@ForeignKey(enabled = true)
@Element(name = "address", required = false)
private Address address;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "user_id", insertable = true, updatable = true, nullable = false)
@ElementList(name = "organisations", required = false)
@ElementDependent
private List<GroupUser> groupUsers = new ArrayList<>();
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@PrimaryKeyJoinColumn(name="sip_user_id", referencedColumnName="id")
@Element(name = "sipUser", required = false)
private AsteriskSipUser sipUser;
// Vars to simulate external Users
@Column(name = "external_id")
@Element(name = "externalUserId", data = true, required = false)
private String externalId;
@Column(name = "external_type")
@Element(name = "externalUserType", data = true, required = false)
private String externalType;
/**
* java.util.TimeZone Id
*/
@Column(name = "time_zone_id")
@Element(data = true, required = false)
private String timeZoneId;
@Transient
private Sessiondata sessionData;
@Column(name = "forceTimeZoneCheck", nullable = false)
@Element(data = true, required = false)
private boolean forceTimeZoneCheck;
@Column(name = "user_offers")
@Element(data = true, required = false)
private String userOffers;
@Column(name = "user_searchs")
@Element(data = true, required = false)
private String userSearchs;
@Column(name = "show_contact_data", nullable = false)
@Element(data = true, required = false)
private boolean showContactData;
@Column(name = "show_contact_data_to_contacts", nullable = false)
@Element(data = true, required = false)
private boolean showContactDataToContacts;
@Column(name = "type")
@Element(data = true, required = false)
@Enumerated(EnumType.STRING)
private Type type = Type.user;
@Column(name = "owner_id")
@Element(data = true, required = false)
private Long ownerId;
@ElementCollection(fetch = FetchType.EAGER)
@Column(name = "om_right")
@CollectionTable(name = "om_user_right", joinColumns = @JoinColumn(name = "user_id"))
@Enumerated(EnumType.STRING)
@ElementList(name="rights", data = true, required = false)
private Set<Right> rights = new HashSet<>();
@Column(name = "domain_id")
@Element(data = true, required = false)
private Long domainId; // LDAP config id for LDAP, OAuth server id for OAuth
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Date getAge() {
return age;
}
public void setAge(Date age) {
this.age = age == null ? new Date() :age;
}
public String getFirstname() {
return firstname;
}
public User setFirstname(String firstname) {
this.firstname = firstname;
return this;
}
public Date getLastlogin() {
return lastlogin;
}
public void setLastlogin(Date lastlogin) {
this.lastlogin = lastlogin;
}
public String getLastname() {
return lastname;
}
public User setLastname(String lastname) {
this.lastname = lastname;
return this;
}
public String getDisplayName() {
return Strings.isEmpty(displayName) ? generateDisplayName() : displayName;
}
public User setDisplayName(String displayName) {
if (!Strings.isEmpty(displayName)) {
this.displayName = escapeMarkup(displayName).toString();
}
return this;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public void updatePassword(String pass) throws NoSuchAlgorithmException {
if (isSipEnabled()) {
AsteriskSipUser u = getSipUser();
if (u == null) {
setSipUser(u = new AsteriskSipUser());
}
String defaultRoomContext = getSipContext();
u.setName(login);
u.setDefaultuser(login);
u.setMd5secret(MD5.checksum(login + ":asterisk:" + pass));
u.setContext(defaultRoomContext);
u.setHost("dynamic");
} else {
setSipUser(null);
}
password = CryptProvider.get().hash(pass);
}
public String getPassword() {
return password;
}
/**
* @deprecated should not be used directly (for bean usage only)
*
* @param password - password to set
*/
@Deprecated
public void setPassword(String password) {
this.password = password;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public Salutation getSalutation() {
return salutation;
}
public void setSalutation(Salutation salutation) {
this.salutation = salutation;
}
public String getPictureUri() {
return pictureUri;
}
public void setPictureUri(String pictureUri) {
this.pictureUri = pictureUri;
}
public long getLanguageId() {
return languageId;
}
public void setLanguageId(long languageId) {
this.languageId = languageId;
}
public List<GroupUser> getGroupUsers() {
return groupUsers;
}
public void setGroupUsers(List<GroupUser> groupUsers) {
if (groupUsers != null) {
this.groupUsers = groupUsers;
}
}
public String getResethash() {
return resethash;
}
public void setResethash(String resethash) {
this.resethash = resethash;
}
public Date getResetDate() {
return resetDate;
}
public void setResetDate(Date resetDate) {
this.resetDate = resetDate;
}
public String getActivatehash() {
return activatehash;
}
public void setActivatehash(String activatehash) {
this.activatehash = activatehash;
}
public String getExternalId() {
return externalId;
}
public void setExternalId(String externalId) {
this.externalId = externalId;
}
public String getExternalType() {
return externalType;
}
public void setExternalType(String externalType) {
this.externalType = externalType;
}
public Sessiondata getSessionData() {
return sessionData;
}
public void setSessionData(Sessiondata sessionData) {
this.sessionData = sessionData;
}
public AsteriskSipUser getSipUser() {
return sipUser;
}
public void setSipUser(AsteriskSipUser sipUser) {
this.sipUser = sipUser;
}
public String getTimeZoneId() {
return timeZoneId;
}
public void setTimeZoneId(String timeZoneId) {
this.timeZoneId = timeZoneId;
}
public boolean getForceTimeZoneCheck() {
return forceTimeZoneCheck;
}
public void setForceTimeZoneCheck(boolean forceTimeZoneCheck) {
this.forceTimeZoneCheck = forceTimeZoneCheck;
}
public String getUserOffers() {
return userOffers;
}
public void setUserOffers(String userOffers) {
this.userOffers = userOffers;
}
public String getUserSearchs() {
return userSearchs;
}
public void setUserSearchs(String userSearchs) {
this.userSearchs = userSearchs;
}
public boolean isShowContactData() {
return showContactData;
}
public void setShowContactData(boolean showContactData) {
this.showContactData = showContactData;
}
public boolean isShowContactDataToContacts() {
return showContactDataToContacts;
}
public void setShowContactDataToContacts(boolean showContactDataToContacts) {
this.showContactDataToContacts = showContactDataToContacts;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Long getOwnerId(){
return ownerId;
}
public void setOwnerId(Long ownerId){
this.ownerId = ownerId;
}
public Set<Right> getRights() {
return rights;
}
public void setRights(Set<Right> rights) {
this.rights = rights;
}
public Long getDomainId() {
return domainId;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
@Override
public String toString() {
return "User [id=" + id + ", firstname=" + firstname
+ ", lastname=" + lastname + ", login=" + login
+ ", pictureuri=" + pictureUri + ", deleted=" + isDeleted()
+ ", languageId=" + languageId + ", address=" + address
+ ", externalId=" + externalId + ", externalType=" + externalType
+ ", type=" + type + "]";
}
private String generateDisplayName() {
StringBuilder sb = new StringBuilder();
String delim = "";
OmLanguage l = LabelDao.getLanguage(languageId);
String first = l.isRtl() ? getLastname() : getFirstname();
String last = l.isRtl() ? getFirstname() : getLastname();
if (!Strings.isEmpty(first)) {
sb.append(first);
delim = " ";
}
if (!Strings.isEmpty(last)) {
sb.append(delim).append(last);
}
return escapeMarkup(sb).toString();
}
}
在user目录下的其他java文件与User.java结构类似,分析方法也类似,只是对应的实体不同。所以,省略了重复的分析过程。因此,user目录也分析完毕了。进一步设想,entity目录下的其他实体目录是不是也可以这样迅速结束呢?可以等下一篇文章再研究。
总结
本文完成了db模块的util目录分析收尾,并且完成了entity目录下user目录的分析,进度向前走了一大步。下一篇文章将会继续完成entity目录的分析,然后开始探究其他目录。