Java EE 6示例– Galleria –第3部分

关于Galleria示例的先前文章( 第1 部分 | 第2部分 | 第3部分 | 第4部分 )指导您完成基础知识以及对GlassFish和WebLogic的初始部署。 从今天开始,我尝试在其中添加一些企业级功能,因为我发现他们在自己的项目中提出了很多要求。 我知道Vineet还将随着时间的推移添加更多功能 ,我希望这不会对读者造成混淆。 但是,让我们看看这是如何实现的,以及我的哪些功能被Vineet所采用,而哪些不是:)。 让我知道您是否想添加任何特别的东西!

会话固定

企业Java应用程序最热门的话题是安全性。 由于它具有许多不同的方面,因此我决定从一个非常简单但经常需要的功能入手:防止会话固定。 这不是Java或JSF特有的,而是基于Web的应用程序的普遍问题。 当会话ID易于发现或猜测时,就会出现会话固定。 攻击的主要方法是URL或响应的任何其他部分中存在会话ID。 攻击者可以捕获一个会话,然后将链接嵌入到其页面中,诱使用户访问该会话并成为其会话的一部分。 然后,当用户认证时,会话即被认证。 在这里使用Cookies只能提供一定的安全性,因为大多数情况下还通过暗示保密性丢失的方法进行设置。 大多数应用服务器会根据第一个请求生成一个新的会话ID。 认证通过后,可以再次使用。 防止这种情况的唯一方法是在成功的身份验证请求之后发出新的随机会话。
一般来说,这很容易做到。 转到galleria-jsf项目并找到info.galleria.view.user.Authenticator bean。 将以下行添加到authenticate()方法的开头:

String result = null;
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();// Session Fixation Prevention
HttpSession session = (HttpSession) externalContext.getSession(false);if (logger.isDebugEnabled()) {logger.debug("Session before authentication request: " + session.getId());}session.invalidate();
session = (HttpSession) externalContext.getSession(true);if (logger.isDebugEnabled()) {logger.debug("Session after authentication request: " + session.getId());}

就是这样 第一次接触代码库就很容易进行更改。 切换到软件包信息info.galleria的调试级别FINE应该在日志文件中揭示魔术:

[#|2012-03-27T17:17:25.298+0200|FINE|glassfish3.1.2|info.galleria.view.user.Authenticator|_ThreadID=27;
_ThreadName=Thread-4;ClassName=info.galleria.view.user.Authenticator;MethodName=
authenticate;|Session before authentication request: 33b1205d7ad740631978ed211bce|#][#|2012-03-27T17:17:25.301+0200|FINE|glassfish3.1.2|info.galleria.view.user.Authenticator
|_ThreadID=27
;_ThreadName=Thread-4;ClassName=info.galleria.view.user.Authenticator;MethodName
=authenticate;|Session after authentication request: 33b1f344ad1730c69bccc35e752e|#]

如预期的那样,我们在身份验证请求期间更改了http会话。 您也可以使用您选择的浏览器插件(在本例中为“编辑此Cookie”)进行检查:

通过执行此操作,Galleria应用程序变得更加安全。 如果您想了解有关会话固定的更多信息,请阅读OWASP页面 。

防止多次登录

下一个要求要复杂一些。 我已经看过几次了,即使对用户来说不方便,出于安全原因也可能是必需的。 正如您可能已经猜到的,没有一个单独的开关。 您必须持有会话图,并检查用户是否已经登录。 在登录过程中应进行检查,并显示有意义的错误消息。

其中有一些棘手的部分。 第一个是,您需要一种方法来存储应用程序的所有用户和HttpSession信息。 第二个是,您需要一个人来照顾它。 让我们从最新开始。

您在这里需要著名的辛格尔顿。 一个地方来存储相关的HttpSession信息。 首先想到的是使用.getExternalContext()。getApplicationMap()。 这可能有效。 我们在此处设置的登录限制有一些副作用。 想象一下,一个用户没有登录就登录并崩溃了他/她的浏览器。 他/她最终将无法重新登录,直到进行一些清理或重新启动应用程序为止。 因此,在HttpSessionListener中访问它也至关重要。 鉴于事实,即JSF ExternalContext是ServletContext,我们在这里很安全。

在继续进行有关聚类的更多讨论之前。 我们将在这里构建一个非集群构造。 根据Servlet规范,上下文属性对于创建它们的JVM是本地的。 因此,如果您在集群环境中运行此命令,将会失去保护,因为您可以在集群的每个节点上进行会话。 使该群集安全将意味着使用数据库,ejb组件或分布式缓存。

转到info.galleria.view.util并创建一个名为SessionConcierge的新最终类。 它需要添加和删除会话的方法。 我们显然需要一些东西来处理应用程序映射。 从addSession方法开始,稍后将从info.galleria.view.user.Authenticator托管Bean中调用该方法:

public static boolean addSession(HttpSession session) {String account = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();String sessionId = session.getId();if (account != null && !getApplicationMap(session).containsKey(account)) {getApplicationMap(session).put(account, sessionId);if (logger.isDebugEnabled()) {logger.debug("Added Session with ID {} for user {}", sessionId, account);}return true;} else {logger.error("Cannot add sessionId, because current logged in account is NULL or session already assigned!");return false;}}

基本上,这将检查我们是否在这里有登录用户,以及该用户是否已经分配了会话。 如果有一个用户并且他没有正在使用的会话,我们将把当前会话添加到该帐户下的应用程序映射中作为键。 接下来一点删除逻辑:

public static void removeSession(HttpSession session) {String sessionId = session.getId();String account = getKeyByValue(getApplicationMap(session), sessionId);if (account != null) {getApplicationMap(session).remove(account);if (logger.isDebugEnabled()) {logger.debug("Removed Session with ID {} for user {}", sessionId, account);}}}

这有点棘手。 您注意到,我使用该帐户作为在地图中绑定会话的密钥。 因此,我必须花一点点技巧来反转地图并通过值找到键。 这个小魔术在这里发生:

private static <T, E> T getKeyByValue(Map<T, E> map, E value) {for (Entry<T, E> entry : map.entrySet()) {if (value.equals(entry.getValue())) {return entry.getKey();}}return null;}

做完了 一件事失踪。 getApplicationMap(HttpSession session)方法。 这不是很神奇。 它只是试图弄清楚我们是否需要通过FacesContext或ServletContext获取它。 如果您感到好奇,请查看SessionConcierge源。 最后要做的是将SessionConcierge添加到Authenticator中 。 将此代码添加到try {request.login()}中(我为您的定位添加了前两行:

request.login(userId, new String(password));result = "/private/HomePage.xhtml?faces-redirect=true";// save sessionId to disable multiple sessions per userif (!SessionConcierge.addSession(session)) {request.logout();logger.error("User {} allready logged in with another session", userId);FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, Messages.getString("Login.AllreadyLoggedIn", locale), null);FacesContext.getCurrentInstance().addMessage(null, facesMessage);}

如果通过SessionConcierge添加HttpSession失败,则立即注销用户并添加FacesMessage。 请记住将其添加到galleria-jsf \ src \ main \ resources \ resources messages.properties及其翻译中。 并且不要忘记添加

SessionConcierge.removeSession(session);

到公共String logout()。 精细。 就是这样,不是吗? 至少它现在正在工作。 但是我们仍然必须解决那些崩溃的浏览器问题。 如果某人未通过该应用程序注销,会话超时或浏览器崩溃,则在重新启动该应用程序之前,您将无法再次登录。 那是不可思议的。 需要某种清理机制。 HttpSessionListener呢? 听起来不错! 将其添加到info.galleria.listeners中,并将其命名为SessionExpirationListener 。

@Overridepublic void sessionDestroyed(HttpSessionEvent se) {HttpSession session = se.getSession();SessionConcierge.removeSession(session);if (logger.isDebugEnabled()) {logger.debug("Session with ID {} destroyed", session.getId());}}

精细。 现在应该可以了。 继续尝试一下。 打开两个不同的浏览器,然后尝试同时登录。 只有一个可以让您访问该应用程序。 第二个应该以您放入messages.properties中的错误消息作为响应。 请注意,这不是多窗口预防措施。 您仍然可以根据需要自由地为每个HttpSession打开尽可能多的窗口。

一个小的补充:如果您严重依赖HttpSessionListener清理,则应确保它具有正确的生存期。 通过产品特定的Web应用程序部署描述符(例如weblogic.xml或glassfish-web.xml)进行配置。 我建议将其设置为合理的较低值(例如30分钟或更短),以免用户等待太长时间。 这是Glassfish(glassfish-web.xml)的外观:

<session-config><session-properties><property name="timeoutSeconds" value="1800" /></session-properties></session-config>

和用于WebLogic(weblogic.xml)

<session-descriptor><timeout-secs>180</timeout-secs></session-descriptor>

Galleria Java EE 6示例应用程序正在增长。 今天,我将写关于如何优雅地处理错误的文章。 关于用户输入验证,已经做了很多工作,但是仍然有很多失败情况没有得到解决,应该解决。 如果您对过去发生的事情感到好奇,请查看本系列的第一部分: 基础知识 , 在GlassFish上 运行,在WebLogic 上 运行 , 测试和增强安全性 。

通用异常机制

应用程序使用检查的异常在层之间传递错误。 ApplicationException是所有可能的业务异常的根源。

这些业务异常在域和表示层之间传达验证冲突和所有已知错误。 galleria-jsf视图项目中的<domain> Manager(例如AlbumManger)类将其捕获,并使用ExceptionPrecessor将错误消息填充到视图中。 在这两层之间可能发生的另一种异常是RuntimeExceptions。 那些被容器包装到EJBException中,并且还被<domain> Manager类捕获。 这些会生成更一般的错误消息,并显示给用户。

在这里,我不会涉及检查与未检查的异常(如果您好奇的话,Google会介绍一下 )。 当应用程序有机会从错误中恢复时,我倾向于使用检查异常。 当某些事情无法恢复时,将引发未经检查的检查。 这就是原因,我对目前内置的异常处理机制不满意。 我稍后再讨论。

有什么不见了? ViewExpired等。

似乎现在一切都已处理。 但只有第一印象。 打开登录屏幕,稍等片刻,让您的http会话超时。 现在,您会看到一个不太漂亮的ViewExpired异常屏幕。

如果您以登录用户的身份尝试登录,则只需将其重定向到登录页面。 无论如何,对于表示层中的一些其他意外情况,可能会出现相同的错误页面。 因此,让我们修复此问题。 最明显的事情是简单地引入专用的错误页面。

<error-page><exception-type>javax.faces.application.ViewExpiredException</exception-type><location>/viewExpired.xhtml</location></error-page>

现在,您将用户重定向到专用页面,该页面可以告诉他/她有关工作场所安全性的一些知识,并且不会使应用长时间处于无人看管状态。 这适用于大多数应用程序。 如果您愿意在页面上获得一些其他信息,或者只是想捕获多个异常并单独处理它们而不必静态配置它们,则需要一种称为ExceptionHandler的东西。 这是JSF 2中的新功能,您所需要做的就是实现ExceptionHandler,并且它是工厂。 工厂本身在facex-config.xml中配置,因为没有任何注释。

打开faces-config.xml,并在底部添加以下几行:

<factory><exception-handler-factory>info.galleria.handlers.GalleriaExceptionHandlerFactory</exception-handler-factory></factory>

现在,我们将在专用包中实现GalleriaExceptionHandlerFactory 。 有趣的方法是:

@Overridepublic ExceptionHandler getExceptionHandler() {ExceptionHandler result = parent.getExceptionHandler();result = new GalleriaExceptionHandler(result);return result;}

每个请求调用一次,每次调用必须返回一个新的ExceptionHandler实例。 在这里,真正的ExceptionHandlerFactory被调用并被要求创建实例,然后将该实例包装在自定义的GalleriaExceptionHandler类中。 这是真正有趣的事情发生的地方。

@Overridepublic void handle() throws FacesException {for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {ExceptionQueuedEvent event = i.next();ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();Throwable t = context.getException();if (t instanceof ViewExpiredException) {ViewExpiredException vee = (ViewExpiredException) t;FacesContext fc = FacesContext.getCurrentInstance();Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();NavigationHandler nav =fc.getApplication().getNavigationHandler();try {// Push some stuff to the request scope for later use in the pagerequestMap.put("currentViewId", vee.getViewId());nav.handleNavigation(fc, null, "viewExpired");fc.renderResponse();} finally {i.remove();}}}// Let the parent handle all the remaining queued exception events.getWrapped().handle();}

使用从getUnhandledExceptionQueuedEvents()。iterator()返回的迭代器迭代非处理程序异常。 ExeceptionQueuedEvent是一个SystemEvent,您可以从中获取实际的ViewExpiredException。 最后,您从异常中提取了一些其他信息,并将其放在请求范围内,以便稍后通过页面中的EL进行访问。 ViewExpiredException要做的最后一件事是使用JSF隐式导航系统(“ viewExpired”解析为“ viewExpired.xhtml”)并通过NavigationHandler导航至“ viewExpired”页面。 不要忘记在finally块中删除已处理的异常。 您不希望父异常处理程序再次处理此问题。 现在,我们必须创建viewExpired.xhtml页面。 在galleria-jsf \ src \ main \ webapp文件夹中执行此操作。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"template="./templates/defaultLayout.xhtml"xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"><ui:define name="title"><h:outputText value="#{msg['Exception.page.title']}" /></ui:define><ui:define name="content"><h:form><h:outputText value="#{msg['Exception.page.message']}" /><p>You were on page #{currentViewId}.  Maybe that's useful.</p><p>Please re-login via the <h:outputLink styleClass="homepagelink" value="#{request.contextPath}/Index.xhtml" ><h:outputText value="Homepage" /></h:outputLink>.</p></h:form></ui:define>
</ui:composition>

请注意,我在此处添加了新的消息属性,因此您需要确保将它们放在galleria-jsf \ src \ main \ resources \ resources \ messages.properties和翻译中。

到目前为止,这显然只处理一种特殊的异常实例。 您可以将其扩展为也可以处理其他内容。 现在我们已经有了基本的机制,您可以自由地执行此操作。

重构RuntimeException处理

如我所说,我对应用程序处理RuntimeExceptions的方式不满意。 现在我们已经有了一个很好的中央异常处理,我们可以将这些内容稍微移动一下并重构* Manager类。 从所有它们中删除所有这些catch(EJBException ejbEx){块。 我们将在一分钟内在GalleriaExceptionHandler中进行处理。 只需将另一个检查添加到GalleriaExceptionHandler即可,如果引发了ViewExpiredException以外的任何其他异常,则将用户重定向到另一个页面。

// check for known Exceptionsif (t instanceof ViewExpiredException) {ViewExpiredException vee = (ViewExpiredException) t;// Push some stuff to the request scope for later use in the pagerequestMap.put("currentViewId", vee.getViewId());} else {forwardView = "generalError";Locale locale = fc.getViewRoot().getLocale();String key = "Excepetion.GeneralError";logger.error(Messages.getLoggerString(key), t);String message = Messages.getString(key, locale);FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null);fc.addMessage(null, facesMessage);}

这种方法具有一些优点。 它减少了* Manager类中所需的代码,并且我们终于有了一个中心位置来处理那些不可恢复的异常。 这还是不是很像企业。 想象一下,您的第一级支持团队需要照顾客户,他们开始抱怨他们收到的唯一消息是“ GeneralError”。 那不是很有帮助。 您的支持团队将需要升级它,第二或第三级需要检查日志和and and ..所有这些都是由于我们已知的错误。 首先要做的是找出导致错误的原因。 解析堆栈跟踪并不是很大的乐趣。 特别是不是包含在EJBExceptions中以及在FacesExceptions中的RuntimeExceptions中。 感谢上帝提供Apache Commons ExceptionUtils 。 打开您的galleria-jsf pom.xml并将其添加为依赖项:

<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency>

现在,您可以开始检查根本原因:

} else {forwardView = "generalError";// no known instance try to specifyThrowable causingEx = ExceptionUtils.getRootCause(t);if (causingEx == null) {causingEx = t;}
//...logger.error(Messages.getLoggerString(key), t);requestMap.put("errorCode", errorCode);

别忘了在这里也记录完整的堆栈跟踪(t,不仅是causeEx)。 通常,让用户知道异常是一件坏事。 没有人真正希望看到错误发生(因为我们讨厌犯错误),并且在所有异常之后,堆栈跟踪都可以泄露您不希望在屏幕上某个地方看到的敏感信息。 因此,您需要找到一种方法来显示对用户有意义的内容,而又不会过多披露。 那就是著名的错误代码起作用的地方。 使用根本原因异常作为消息键,或自行决定要为此付出的努力。 它可能是一个错误类别的系统(数据库,接口系统等),它们为第一级支持提供了有关导致错误的原因的良好提示。 从一开始我就坚持一个简单的解决方案。 只需为每个捕获的异常生成一个UUID并将其跟踪到日志和UI。 以下是一个非常简单的示例。

String errorCode = String.valueOf(Math.abs(new Date().hashCode()));

这也应该添加到消息属性中,并且不要忘记,您还需要另一个用于generalError模板。 如果slf4j将使用与jdk日志记录相同的消息格式,那么您只需要一个属性..

Exception.generalError.log=General Error logged: {}.
Exception.generalError.message=A general error with id {0} occured. Please call our hotline.

将此添加到generalError.xhtml并查看如何将错误代码传递到消息模板。

<h:outputFormat  value="#{msg['Exception.generalError.message']}" ><f:param value="#{errorCode}"/></h:outputFormat>

这里还有很多需要改进的地方。 您可以使用javax.faces.application.ProjectStage查找应用程序正在运行的当前模式。如果您在ProjectStage.Development中运行,则还可以将完整的堆栈跟踪信息放到UI上,并使调试工作变得容易一些。 以下代码段尝试从JNDI获取ProjectStage。

public static boolean isProduction() {ProjectStage stage = ProjectStage.Development;String stageValue = null;try {InitialContext ctx = new InitialContext();stageValue = (String) ctx.lookup(ProjectStage.PROJECT_STAGE_JNDI_NAME);stage = ProjectStage.valueOf(stageValue);} catch (NamingException | IllegalArgumentException | NullPointerException e) {logger.error("Could not lookup JNDI object with name 'javax.faces.PROJECT_STAGE'. Using default 'production'");}return ProjectStage.Production == stage;}

那三位数的Http错误页呢?

那是另一件事。 其余所有3位http错误代码,这些错误代码将返回看起来不太好看的错误页面之一。 唯一要做的就是将它们映射到web.xml中,如下所示:

<error-page><error-code>404</error-code><location>/404.xhtml</location></error-page>

您应该确保已放置这些映射,并向用户显示有意义的错误。 始终提供一种从那里进一步导航的方法应该成为最佳实践。

参考: Java EE 6示例–使用Galleria增强安全性–第5部分 , Java EE 6示例–优雅地处理Galleria中的错误–我们JCG合作伙伴 Markus Eisele在Java企业软件开发博客上的第6部分 。


翻译自: https://www.javacodegeeks.com/2012/04/java-ee-6-example-galleria-part-3.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Windows 上测试 Redis Cluster的集群填坑笔记

redis 集群实现的原理请参考http://www.tuicool.com/articles/VvIZje集群环境至少需要3个节点。推荐使用6个节点配置&#xff0c;即3个主节点&#xff0c;3个从节点。新建6个文件夹 分别是 7000/7001/7002/7003/7004/7005将redis.windows.conf 复制一份然后修改配置文件中的下面…

拓扑排序最长链-P3119 [USACO15JAN]草鉴定Grass Cownoisseur

https://www.luogu.org/problem/show?pid3119 本来我是来练习tarjan的&#xff0c;结果tarjan部分直接copy了&#xff0c;反而拓扑排序部分想了好久&#xff1b; 这道题SZB大神两次就AC&#xff1b; 但我等到AC&#xff0c;写好题解就只能洗洗睡了&#xff1b; 唉~ 差距怎…

IBM JVM调整– gencon GC策略

本文将向您详细介绍从Java虚拟机&#xff08;例如HotSpot或JRockit&#xff09;迁移到IBM JVM时重要的Java堆空间调整注意事项。 该调整建议基于我为我的一个IT客户端执行的最新故障排除和调整任务。 IBM JVM概述 正如您可能从其他文章中看到的那样&#xff0c;IBM JVM在某些方…

懒惰的JSF Primefaces数据表分页–第2部分

页面代码非常简单&#xff0c;没有复杂性。 检查“ index.xhtml”代码&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www…

java实现报表_用存储过程和 JAVA 写报表数据源有什么弊端?

用存储过程和 JAVA 写报表数据源有什么弊端&#xff1f;跟着小编一起来一看一下吧&#xff01;我们在报表开发中经常会使用存储过程准备数据&#xff0c;存储过程支持分步计算&#xff0c;可以实现非常复杂的计算逻辑&#xff0c;为报表开发带来便利。所以&#xff0c;报表开发…

SpringMVC学习笔记整理

SpringMVC学习笔记 以下是我整理的SpringMVC学习笔记&#xff1a; 导入jar包 一&#xff1a;springmvc工作流程。 ①. servlet容器初始化一个request请求 ②. DispatcherServlet分发器负责发送请求到映射器. ③. despatcherServlet把请求交给处理器映射Mapping&…

springboot2 使用hikaridatasource 并测试_基于Spring Boot 2.x的后端管理网站脚手,源码免费分享...

基于Spring Boot 2.x 的 Material Design 的后端管理网站脚手架 &#xff1a;提供权限认证 用户管理 菜单管理 操作日志 等常用功能去繁就简 重新出发基于Spring Boot 集成一些常用的功能&#xff0c;你只需要基于它做些简单的修改即可。功能列表&#xff1a;权限认证权限管理用…

测试驱动开发–双赢策略

敏捷从业人员谈论测试驱动开发 &#xff08;TDD&#xff09;&#xff0c;所以许多关心代码质量和可操作性的开发人员也是如此。 我曾几何时&#xff0c;不久前设法阅读了有关TDD的文章。 据我了解&#xff0c;TDD的关键是&#xff1a; 编写测试&#xff0c;但失败 代码&#x…

设计模式学习(三)——装饰器模式

前言 距离上一次正儿八经地写随笔已经有一段时间了&#xff0c;虽然2月10号有一篇关于泛型的小记&#xff0c;但是其实只是简单地将自己的学习代码贴上来&#xff0c;为了方便后续使用时查阅&#xff0c;并没有多少文字和理解感悟。之所以在今天觉得有必要写点东西&#xff0c;…

PCL学习八叉树

建立空间索引在点云数据处理中有着广泛的应用&#xff0c;常见的空间索引一般 是自顶而下逐级划分空间的各种空间索引结构&#xff0c;比较有代表性的包括BSP树&#xff0c;KD树&#xff0c;KDB树&#xff0c;R树&#xff0c;四叉树&#xff0c;八叉树等索引结构&#xff0c;而…

Android实现自定义带文字和图片的Button

在Android开发中经常会需要用到带文字和图片的button&#xff0c;下面来讲解一下常用的实现办法。 一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现&#xff0c;这种方式代码量最小。在Button的属性中有一个是drawableLeft&#xff0c;这个 属性可以…

mysql语句中的注释方法_MySQL语句注释方式简介

MySQL支持三种注释方式&#xff1a;1.从‘#字符从行尾。2.从‘-- 序列到行尾。请注意‘-- (双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如空格、tab、换行符等等)。3.从/*序列到后面的*/序列。结束序列不一定在同一行中&#xff0c;因此该语法允许注释跨越多行。…

android框架----下沉文字Titanic的使用

Titanic is a simple illusion obtained by applying an animated translation on the TextView TextPaint Shaders matrix. Titanic的使用 Titanic的使用&#xff0c;项目结构如下&#xff1a; 一、下载Titanic并且部署到项目中 Titanic的项目地址&#xff1a; https://github…

linux 自动安装mysql_Linux安装mysql

一、下载这里我创建了一目录software用于存放我们待会要下载的mysql包&#xff0c;先去到该目录命令&#xff1a;cd /software命令&#xff1a;wget http://mirrors.sohu.com/mysql/MySQL-5.7/mysql-5.7.17-linux-glibc2.5-x86_64.tar下载完成后&#xff0c;你会在software这个…

Animation用法

测试代码及说明&#xff1a; <!DOCTYPE html> <html lang"en-US"> <head><meta charset"UTF-8"><title>Simple CSS3 Animation</title><style type"text/css">#demo {position: absolute;left: 30%;t…

mysql备份 where_MySQL备份与还原

1.mysqldumpmysqlbinlog介绍mysqldump备份结合binlog日志恢复。MySQL备份一般采取全库备份加日志备份的方式&#xff0c;例如每天执行一次全备份&#xff0c;每小时执行一次二进制日志备份&#xff0c;这样在MySQL故障后可以使用全备份和日志备份将数据恢复到最后一个二进制日志…

JMeter:负载测试关系数据库

Apache JMeter是完全使用Java编写的性能测试工具。 可以在请求/响应模型上运行的任何应用程序都可以使用JMeter进行负载测试。 关系数据库也不例外&#xff1a;接收sql查询&#xff0c;执行它们并返回执行结果。 我将向您展示使用JMeter的图形用户界面设置测试方案有多么容易。…

new: Set up a window

Nehe的教程确实太老了&#xff0c;不过我认为它也能够让我了解OpenGL3.2以前的管线渲染模式&#xff0c;即使它在现在已经不常见了。因为想要了解&#xff0c;所以我还是会看完Nehe的教程。 现在这是一个新的教程 - JoeyDeVries的教程&#xff0c;可以说是网上最好的OpenGL教程…

Python全栈开发:socket

Socket socket通常也称作"套接字"&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄&#xff0c;应用程序通常通过"套接字"向网络发出请求或者应答网络请求。 socket起源于Unix&#xff0c;而Unix/Linux基本哲学之一就是“一切皆文件”&…

NetBeans 7.1:创建自定义提示

我已经在帖子中介绍了一些我最喜欢的NetBeans提示 &#xff0c;这些信息是用于使Java代码现代化的七个NetBeans提示和七个不可或缺的NetBeans Java提示 。 这两个帖子中涉及的十四个提示仅占NetBeans支持的“即开即用”提示总数的一小部分。 但是&#xff0c;由于NetBeans 7.1使…