Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)
一、缘由
数据库方言可能在当前应用的架构中意义不是很大,但是如果单纯用MyBatis或Hibernate做数据库持久化操作,还是要处理这一问题。
那么下面将介绍怎么样动态切换SessionFactory,为什么要切换SessionFactory?
因为这里切换SessionFactory就可以实现多数据源和多个SessionFactory,每个SessionFactory有自己独立的数据库配置和SessionFactory的相关配置。我们的数据库方言就配置在SessionFactory这里,所以实现了切换SessionFactory也就实现了切换数据库方言的问题。这个主要是针对Hibernate来操作的,而MyBatis则需要动态切换SqlSessionFactory才行。
二、实现代码
1、定义全局切换SessionFactory的工具
package com.hoo.framework.spring.support; /** * <b>function:</b> 多数据源 * @author hoojo * @createDate 2013-9-27 上午11:36:57 * @file CustomerContextHolder.java * @package com.hoo.framework.spring.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public abstract class CustomerContextHolder { public final static String SESSION_FACTORY_MYSQL = "mysql"; public final static String SESSION_FACTORY_ORACLE = "oracle"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setCustomerType(String customerType) { contextHolder.set(customerType); } public static String getCustomerType() { return contextHolder.get(); } public static void clearCustomerType() { contextHolder.remove(); } }同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。
2、实现自己的SessionFactory
定义好接口
package com.hoo.framework.spring.support.core; import org.hibernate.SessionFactory; /** * <b>function:</b> 动态SessionFactory接口 * @author hoojo * @createDate 2013-10-12 下午03:29:52 * @file DynamicSessionFactory.java * @package com.hoo.framework.spring.support.core * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface DynamicSessionFactory extends SessionFactory { public SessionFactory getHibernateSessionFactory(); }
实现接口
package com.hoo.framework.spring.support.core; import java.io.Serializable; import java.sql.Connection; import java.util.Map; import java.util.Set; import javax.naming.NamingException; import javax.naming.Reference; import org.hibernate.Cache; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.SessionFactory; import org.hibernate.StatelessSession; import org.hibernate.TypeHelper; import org.hibernate.classic.Session; import org.hibernate.engine.FilterDefinition; import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.stat.Statistics; import com.hoo.framework.spring.support.CustomerContextHolder; /** * <b>function:</b> 动态数据源实现 * @author hoojo * @createDate 2013-10-12 下午03:31:31 * @file DynamicSessionFactoryImpl.java * @package com.hoo.framework.spring.support.core * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @SuppressWarnings({ "unchecked", "deprecation" }) public class DynamicSessionFactoryImpl implements DynamicSessionFactory { private static final long serialVersionUID = 5384069312247414885L; private Map<Object, SessionFactory> targetSessionFactorys; private SessionFactory defaultTargetSessionFactory; /** * @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory() * <b>function:</b> 重写这个方法,这里最关键 * @author hoojo * @createDate 2013-10-18 上午10:45:25 */ @Override public SessionFactory getHibernateSessionFactory() { SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType()); if (targetSessionFactory != null) { return targetSessionFactory; } else if (defaultTargetSessionFactory != null) { return defaultTargetSessionFactory; } return null; } @Override public void close() throws HibernateException { this.getHibernateSessionFactory().close(); } @Override public boolean containsFetchProfileDefinition(String s) { return this.getHibernateSessionFactory().containsFetchProfileDefinition(s); } @Override public void evict(Class clazz) throws HibernateException { this.getHibernateSessionFactory().evict(clazz); } @Override public void evict(Class clazz, Serializable serializable) throws HibernateException { this.getHibernateSessionFactory().evict(clazz, serializable); } @Override public void evictCollection(String s) throws HibernateException { this.getHibernateSessionFactory().evictCollection(s); } @Override public void evictCollection(String s, Serializable serializable) throws HibernateException { this.getHibernateSessionFactory().evictCollection(s, serializable); } @Override public void evictEntity(String entity) throws HibernateException { this.getHibernateSessionFactory().evictEntity(entity); } @Override public void evictEntity(String entity, Serializable serializable) throws HibernateException { this.getHibernateSessionFactory().evictEntity(entity, serializable); } @Override public void evictQueries() throws HibernateException { this.getHibernateSessionFactory().evictQueries(); } @Override public void evictQueries(String queries) throws HibernateException { this.getHibernateSessionFactory().evictQueries(queries); } @Override public Map<String, ClassMetadata> getAllClassMetadata() { return this.getHibernateSessionFactory().getAllClassMetadata(); } @Override public Map getAllCollectionMetadata() { return this.getHibernateSessionFactory().getAllClassMetadata(); } @Override public Cache getCache() { return this.getHibernateSessionFactory().getCache(); } @Override public ClassMetadata getClassMetadata(Class clazz) { return this.getHibernateSessionFactory().getClassMetadata(clazz); } @Override public ClassMetadata getClassMetadata(String classMetadata) { return this.getHibernateSessionFactory().getClassMetadata(classMetadata); } @Override public CollectionMetadata getCollectionMetadata(String collectionMetadata) { return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata); } @Override public Session getCurrentSession() throws HibernateException { return this.getHibernateSessionFactory().getCurrentSession(); } @Override public Set getDefinedFilterNames() { return this.getHibernateSessionFactory().getDefinedFilterNames(); } @Override public FilterDefinition getFilterDefinition(String definition) throws HibernateException { return this.getHibernateSessionFactory().getFilterDefinition(definition); } @Override public Statistics getStatistics() { return this.getHibernateSessionFactory().getStatistics(); } @Override public TypeHelper getTypeHelper() { return this.getHibernateSessionFactory().getTypeHelper(); } @Override public boolean isClosed() { return this.getHibernateSessionFactory().isClosed(); } @Override public Session openSession() throws HibernateException { return this.getHibernateSessionFactory().openSession(); } @Override public Session openSession(Interceptor interceptor) throws HibernateException { return this.getHibernateSessionFactory().openSession(interceptor); } @Override public Session openSession(Connection connection) { return this.getHibernateSessionFactory().openSession(connection); } @Override public Session openSession(Connection connection, Interceptor interceptor) { return this.getHibernateSessionFactory().openSession(connection, interceptor); } @Override public StatelessSession openStatelessSession() { return this.getHibernateSessionFactory().openStatelessSession(); } @Override public StatelessSession openStatelessSession(Connection connection) { return this.getHibernateSessionFactory().openStatelessSession(connection); } @Override public Reference getReference() throws NamingException { return this.getHibernateSessionFactory().getReference(); } public void setTargetSessionFactorys(Map<Object, SessionFactory> targetSessionFactorys) { this.targetSessionFactorys = targetSessionFactorys; } public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) { this.defaultTargetSessionFactory = defaultTargetSessionFactory; } }上面最重要的就是getHibernateSessionFactory重写这个方法,其他方法和原来实现的无异。重写这个方法后利用CustomerContextHolder动态设置SessionFactory类型就可以动态的切换SessionFactory。
3、动态的事务管理器,因为我们这里是动态切换SessionFactory,所以事务这块也需要动态切换SessionFactory来完成事务的操作。
package com.hoo.framework.spring.support.tx; import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateTransactionManager; import org.springframework.orm.hibernate3.SessionFactoryUtils; import com.hoo.framework.spring.support.core.DynamicSessionFactory; /** * <b>function:</b> 重写HibernateTransactionManager事务管理器,实现自己的动态的事务管理器 * @author hoojo * @createDate 2013-10-12 下午03:54:02 * @file DynamicTransactionManager.java * @package com.hoo.framework.spring.support.tx * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class DynamicTransactionManager extends HibernateTransactionManager { private static final long serialVersionUID = -4655721479296819154L; /** * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource() * <b>function:</b> 重写 * @author hoojo * @createDate 2013-10-12 下午03:55:24 */ @Override public DataSource getDataSource() { return SessionFactoryUtils.getDataSource(getSessionFactory()); } /** * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory() * <b>function:</b> 重写 * @author hoojo * @createDate 2013-10-12 下午03:55:24 */ @Override public SessionFactory getSessionFactory() { DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory(); SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory(); return hibernateSessionFactory; } }这里主要重写getDataSource()/getSessionFactory()这两个方法,getSessionFactory方法是利用我们上面定义的接口来动态获取我们在上下文(CustomerContextHolder)中定义切换的SessionFactory对象。而getDataSource则是获得动态SessionFactory的DataSource,这里也不难理解。
4、至此,重写的接口和实现都完成,下面开始配置相关的代码
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 配置c3p0数据源 --> <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${datasource.driver}"/> <property name="jdbcUrl" value="${datasource.url}"/> <property name="user" value="${datasource.username}"/> <property name="password" value="${datasource.password}"/> <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> <property name="minPoolSize" value="${c3p0.minPoolSize}"/> <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> <property name="maxStatements" value="${c3p0.maxStatements}"/> <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> </bean> <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/> <property name="user" value="root"/> <property name="password" value="jp2011"/> <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> <property name="minPoolSize" value="${c3p0.minPoolSize}"/> <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> <property name="maxStatements" value="${c3p0.maxStatements}"/> <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> </bean> <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 --> <bean id="mySQLSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSourceMySQL"/> <property name="packagesToScan" value="com.hoo.**.mysqlentity"/> <property name="annotatedClasses"> <array> <value>com.hoo.common.entity.IDGenerator</value> </array> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <!-- 链接释放策略 on_close | after_transaction | after_statement | auto --> <prop key="hibernate.connection.release_mode">after_transaction</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> </props> </property> </bean> <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 --> <bean id="oracleSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSourceOracle"/> <property name="packagesToScan" value="com.hoo.**.entity"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> <prop key="hibernate.connection.release_mode">after_transaction</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <!--prop key="hibernate.hbm2ddl.auto">update</prop--> </props> </property> </bean> <!-- 动态SessionFactory --> <bean id="sessionFactory" class="com.hoo.framework.spring.support.core.DynamicSessionFactoryImpl"> <property name="defaultTargetSessionFactory" ref="oracleSessionFactory"/> <property name="targetSessionFactorys"> <map> <entry value-ref="oracleSessionFactory" key="oracle"/> <entry value-ref="mySQLSessionFactory" key="mysql"/> </map> </property> </bean> <!-- 自定义动态切换SessionFactory事务管理器,注入sessionFactory --> <bean id="transactionManager" class="com.hoo.framework.spring.support.tx.DynamicTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置事务的传播特性 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="*" read-only="true" /> </tx:attributes> </tx:advice> <!-- 配置那些类、方法纳入到事务的管理 --> <aop:config> <aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" /> </aop:config> </beans>配置也和我们之前的配置差不多,就是事务管理器部分注入的SessionFactory是我们自己定义的。
5、简单测试
@Test public void testAdd() { // 主要就是这行代码 它才是完成SessionFactory的切换 CustomerContextHolder.setCustomerType(CustomerContextHolder.SESSION_FACTORY_MYSQL); DeviceInfo entity = new DeviceInfo(); entity.setSbbh(System.currentTimeMillis() + ""); entity.setIpdz("my ip address2"); entity.setJd(1234); try { service.add(entity); } catch (Exception e) { e.printStackTrace(); } }经过测试发现可以查询数据,利用hibernate查询分页也可以生成对应数据库的分页语句。同样在服务层的方法中添加完操作后,特意抛出一个异常,数据也能正常回滚操作,所以DynamicTransactionManager也是起到了该有的作用!
这里我的测试用例是手动切换CustomerContextHolder.setCustomerType的,但实际开发中我们还是得用Spring的Aop中的Interceptor进行切面编程,完成动态切换SessionFactory。上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法已经提到了(读者可以参考该篇博文中的第二节的3、7小节DataSourceMethodInterceptor MultipleDataSourceInterceptor),这里就不再赘述!
三、引发的问题
上述的实现如果在使用不当的情况下,在实际开发中很可能存在一些问题!
问题1是这样的,通常事务是在Service这层完成,这个应该是没有异议。倘若是这样的话,问题便出现了。而通常我们在MVC中的视图层中仅仅会调用Service中一个方法来完成所有当前业务的处理。如果这个Service中同时操作dbA、dbB两个数据库,事务提交的时候是用哪个数据库的事务呢?所以我们把不同数据库的操作放在一个方法,就会出现事务的问题的,除非两个数据库的事务能够同时回滚!
大致情景是:Service中的add4Oracle操作Oracle数据库,Service中的add4MySQL操作MySQL数据库,最后把Service中的add4Oracle、add4MySQL方法放到一个operation方法中,MVC视图层的业务控制调用Service中的operation。像这样的情况,如果add4Oracle或add4MySQL方法中的某一个方法出现异常,就需要把两个数据库事务都回滚。
解决办法就是在Service或Dao中的一个方法中同时操作两个数据库,手动完成事务控制。出现异常就全部回滚,没有异常就全部提交,只能这样牺牲下。
问题2是利用拦截器不能同时切换一个方法操作两个数据库的可能,例如一个service中的query方法即需要用query MySQL,也需要query Oracle。那么解决办法就是在query方法中调用当前service的query4Oracle和query4Oracle,那样绕过去还是能利用拦截器进行动态切换的。
四、总结
要完成数据库方言的切换,我们就需要配置多个SessionFactory利用自己实现的DynamicSessionFactory和CustomerContextHolder完成SessionFactory的切换。利用DynamicTransactionManager完成当前Session的事务操作。通过对这些对象的操作和配置,最终可以完成SessionFactory的动态切换。实际使用中虽然有些问题出现,但是最终还是有解决方案,尽管有些不完美。所谓的完美的东西总有它不完美的地方,这样才算完美,比如断臂的维纳斯女神~!
本文固定链接: http://www.devba.com/index.php/archives/3148.html | 开发吧
相关推荐
起点小说解锁.js
299-煤炭大数据智能分析解决方案.pptx
299-教育行业信息化与数据平台建设分享.pptx
网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes
时间复杂度是计算机科学中用来评估算法效率的一个重要指标。它表示了算法执行时间随输入数据规模增长而变化的趋势。当我们比较不同算法的时间复杂度时,实际上是在比较它们在不同输入规模下的执行效率。 时间复杂度通常用大O符号来表示,它描述了算法执行时间上限的增长率。例如,O(n)表示算法执行时间与输入数据规模n呈线性关系,而O(n^2)则表示算法执行时间与n的平方成正比。当n增大时,O(n^2)算法的执行时间会比O(n)算法增长得更快。 在比较时间复杂度时,我们主要关注复杂度的增长趋势,而不是具体的执行时间。这是因为不同计算机硬件、操作系统和编译器等因素都会影响算法的实际执行时间,而时间复杂度则提供了一个与具体实现无关的评估标准。 一般来说,时间复杂度越低,算法的执行效率就越高。因此,在设计和选择算法时,我们通常希望找到时间复杂度尽可能低的方案。例如,在排序算法中,冒泡排序的时间复杂度为O(n^2),而快速排序的时间复杂度在平均情况下为O(nlogn),因此在处理大规模数据时,快速排序通常比冒泡排序更高效。 总之,时间复杂度是评估算法效率的重要工具,它帮助我们了解算法在不同输入规模下的性
5G通信行业、网络优化、通信工程建设资料
网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes
Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。
5G通信行业、网络优化、通信工程建设资料
《基于小程序的交友系统的设计与实现》是一个融合了小程序技术和社交功能的毕业设计项目。该项目旨在通过开发一款小程序,为用户提供一个便捷、有趣的交友平台,满足用户寻找新朋友、拓展社交圈的需求。 一、项目背景与目标 随着移动互联网的普及,小程序以其轻便、易用的特性受到了广大用户的喜爱。本项目旨在利用小程序技术开发一款交友系统,通过简洁明了的界面设计和丰富多样的社交功能,吸引用户参与并提升用户体验。通过实现这一系统,旨在帮助用户拓展社交圈,增进人际关系,并推动社交领域的创新与发展。 二、系统设计与功能实现 用户注册与登录:系统提供用户注册与登录功能,确保用户信息的真实性和安全性。用户可以通过手机号或第三方社交账号进行注册和登录。 个人资料展示:用户可以在个人资料页面展示自己的基本信息、兴趣爱好、照片等,以便其他用户了解并产生互动。 附近的人:系统通过定位功能展示附近的其他用户,用户可以浏览附近的人的信息,并主动发起聊天或交友请求。 聊天功能:系统提供一对一的聊天功能,用户可以与感兴趣的人进行实时交流,增进彼此的了解。 活动组织:用户可以发起或参与各类线下活动,如聚会、运动、旅行
5G通信行业、网络优化、通信工程建设资料
shampoo-sales.csv
59-《煤矿测量规程(1989版)》150.pdf
Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。
Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。
5G通信行业、网络优化、通信工程建设资料。
AXIS T864 系列多通道 AXIS T8646 PoE+ 同轴电缆刀片套件 AXIS T8648 PoE+ 同轴电缆刀片紧凑型套件安装指南
MATLAB学习个人笔记总结.7z
【资源说明】【毕业设计】 1、该资源内项目代码都是经过测试运行成功,功能正常的情况下才上传的,请放心下载使用。 2、适用人群:主要针对计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、数学、电子信息等)的同学或企业员工下载使用,具有较高的学习借鉴价值。 3、不仅适合小白学习实战练习,也可作为大作业、课程设计、毕设项目、初期项目立项演示等,欢迎下载,互相学习,共同进步!
5G通信、网络优化与通信建设