精华内容
下载资源
问答
  • 多线程实现多个用户访问服务器端

    千次阅读 2019-01-02 16:40:48
    使用多线程实现多个用户访问服务器端,服务器显示用户登录信息并提示用户登录成功· 服务器代码如下: 接下来是自定义的ThreadLogin类,实现了Runnable接口 User类(注意-要实现Serializable接口,不然无法实现序列...

    本次作为一个刚入门的·程序员第一次给大家分享一些学习干货,以后会持续更新…
    案例
    使用多线程实现多个用户访问服务器端,服务器显示用户登录信息并提示用户登录成功·
    服务器代码如下:
    在这里插入图片描述接下来是自定义的ThreadLogin类,实现了Runnable接口
    在这里插入图片描述
    User类(注意-要实现Serializable接口,不然无法实现序列化):
    在这里插入图片描述
    客户端1:
    在这里插入图片描述
    客户端2,3只需要把User传的有参构造改下方法就可以啦^^
    最后,奉上运行结果图:(小提示:先启动服务器,再启动客户端)
    server运行结果图:
    在这里插入图片描述
    客户端:
    在这里插入图片描述
    希望能帮到有需要的小伙伴!!!

    展开全文
  • 数据库访问实现过程

    千次阅读 2016-03-29 21:53:29
    数据库,现在社会中大家都知道是一非常重要的数据管理软件系统,在这里我们进行一次Access数据库访问操作,在正式介绍之前,我们先了接一下数据库访问的过程,在数据库访问中,根据最普遍的想法,需知道的构件...

            数据库是非常重要的数据管理软件系统,在这里我们进行一次Access数据库的访问操作,在正式介绍之前,我们先了解一下数据库访问的过程,在数据库访问中,根据最普遍的想法,需知道的构件主要有三层:分别为客户实体(即软件的图形界面)、访问的中介(即连接数据库与实体的部分)、数据库后台。在了解这些之后,我们现在也清楚了实施这个访问过程我们该做哪些工作,那就是画一下用户使用的界面,部署一个存储数据的仓库,再找一个联系两

    者的链条。下面我们就分别去做这些工作。

    一、数据库的设计

          数据库的种类有很多种,比如著名的Oracle,SQL server,access、Sybase等,各类软件都有自己的优点,oracle适合于大型公司大量数据的管理,Microsoft SQL Server是微软公司开发的大型关系型数据库系统。SQL Server的功能比较全面,效率高,可以作为中型企业或单位的数据库平台。SQL Server可以与Windows操作系统紧密集成,不论是应用程序开发速度还是系统事务处理运行速度,都能得到较大的提升。

           废话不多说,今天我们以Access数据库进行操作,建立ODBC的数据库连接

         1.1建立ODBC数据源

             1.1.1首先,让我们做一点准备工作,那就是在自己的电脑上安装Access数据库软件,软件的安装包在网上有很多,可自行下载安装,详细的安装过程在此就不累赘,如有问题请私密,正确安装完成后可在桌面上看到下面所示的图标


    图1.1.1

    右键点击,进入打开文件位置,可看到


    图1.1.2


    建立一个名为Employer.mdb的Access数据库文件,存放到硬盘上面,记住他的存储路径比如说建在E:\DesktopFile\学习资料Java\Java EE\ODBC_Employer这个文件夹下,如图所示

    图1.1.3

    下面进行详细介绍:首先双击microsoft Access出现创建新文件的窗口,然后点击右下角的文件夹图标景行存储位置的选定,在选定过程中会要求更名,按照所创数据库的类型更名,在这里我们写作Employer,然后点击确定按钮进行确定,接着点击右下角的创建按钮进行数据库的创建,如下图所示,为创建的具体过程


    图1.1.4

     在上述步骤完成后,进入到表的创建界面,先确定要建表的属性及表项,在这里我们以员工信息为例,在第一行中要进行信息类型的确定,我们按常规取ID位数字,REGISTERDATE为日期,其余都为文本类型,进行如下所示的创建,为了方便以后操作先填入4个基本人员信息,


    图1.1.5


    至此,我们完成了对数据库的创建,接下来我们进行可视化界面的实现。

    二、可视化界面的实现

           在实现可视化界面之前,我们做一点部署,对于大多数人来说,编写Java程序一般都使用Eclipse或者Myeclipse等软件,在这里我们采用开源免费的eclipse进行编程,如下图所示,安装过程在此不多介绍,如有问题请私密。


    图2.1.1

          现在,我们开始程序代码的编写,双击打开eclipse,进入文本编辑界面(打开时可能要配置存储路径,随便存储在自己容易识别的路径下就行),点击File->New->Java Project,如下图2.1.1所示,我们将工程名命名为Java_Employer,最后点击Finish完成对新项目的创建.


    图2.1.1

        接着进行主界面的创建,右键点击工程名 ,然后按New->class,然确定包名为Java_Employer,类名为MainFrame,再勾选主函数多选框,最后点击Finish完成按钮,如下图2.1.2所示

    Mian

    图2.1.2

     创建完主框架类之后,我们开始编程,我们要做的是先显示一个主框架,然后框架上面有功能键,在这里我们只是对插入、删除、更新、查询的操作,所以在这里我们需要做的有4个按钮,然后还需要显示文本,所以我们再加入一个文本域,下面是具体实施的代码。

    package Java_Employer;
    import java.awt.FlowLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    public class MainFrame extends JFrame{
    	/**
    	 *            实现框架
    	 */
    	static MainFrame frm_sql = new MainFrame();
    	static JButton Btn_Insert = new JButton("插入");
    	static JButton Btn_Delete = new JButton("删除");
    	static JButton Btn_Update = new JButton("修改");
    	static JButton Btn_Query = new JButton("查询");
    	static JTextArea jTextArea_list = new JTextArea(34,120);
    	static JTextField jTextField_list_question = new JTextField(120);
    	public static void main(String[] args) throws Exception {
    		frm_sql.add(Btn_Delete);
    		frm_sql.add(Btn_Insert);
    		frm_sql.add(Btn_Query);
    		frm_sql.add(Btn_Update);
    		/**
    		 * 设置列表框和问题提示框
    		 */
                    jTextArea_list.setAutoscrolls(true);
    		frm_sql.add(jTextArea_list);//添加jTextArea_List到框架中
    		frm_sql.add(jTextField_list_question);	
    		/**
    		 * 框架布局设置及属性
    		 */
    		frm_sql.setLocation(2, 2);//位置
    		frm_sql.setSize(1366, 730);//大小
    		frm_sql.setLayout(new FlowLayout());//布局属性
    		frm_sql.setVisible(true);
    	}
    }

    在执行完这段代码之后。我们能够看到主框架已经显示出来,接下来需要做的事实施监听事件,因此我们需要在上面这段代码的基础上进行改造,首先将按钮加入到监听事件中,然后就会出现提示,点击后会做出相应的响应函数,再点击删除后会在控制台输出删除二字,其他三个字符类似,代码如下所示(代码基本没有发生变化,只是添加了按钮的监听事件与响应事件):

    package Java_Employer;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    public class MainFrame extends JFrame implements ActionListener {
    	/**
    	 * 实现框架
    	 */
    	static MainFrame frm_sql = new MainFrame();
    	static JButton Btn_Insert = new JButton("插入");
    	static JButton Btn_Delete = new JButton("删除");
    	static JButton Btn_Update = new JButton("修改");
    	static JButton Btn_Query = new JButton("查询");
    	static JTextArea jTextArea_list = new JTextArea(34,120);
    	static JTextField jTextField_list_question = new JTextField(120);
    	public static void main(String[] args) throws Exception {
    	/**
    	 * 功能按钮的实现	
    	 */
    		Btn_Insert.addActionListener(frm_sql);
    		Btn_Delete.addActionListener(frm_sql);
    		Btn_Update.addActionListener(frm_sql);
    		Btn_Query.addActionListener(frm_sql);
    		frm_sql.add(Btn_Delete);
    		frm_sql.add(Btn_Insert);
    		frm_sql.add(Btn_Query);
    		frm_sql.add(Btn_Update);
    		/**
    		 * 设置列表框和问题提示框
    		 */
            jTextArea_list.setAutoscrolls(true);
    		frm_sql.add(jTextArea_list);//添加jTextArea_List到框架中
    		frm_sql.add(jTextField_list_question);	
    		/**
    		 * 框架布局设置及属性
    		 */
    		frm_sql.setLocation(2, 2);//位置
    		frm_sql.setTitle("Access数据库的访问");
    		frm_sql.setSize(1366, 730);//大小
    		frm_sql.setLayout(new FlowLayout());//布局属性
    		frm_sql.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frm_sql.setVisible(true);
    		
    	}
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		String command = e.getActionCommand();
    		if (command.equals("删除")) {
    			System.out.println("删除");
    		}
    		if (command.equals("插入")) {
    			System.out.println("插入");
    		}
    		if (command.equals("修改")) {
    			System.out.println("修改");
    		}
    		if (command.equals("查询")) {
    			System.out.println("查询");
    		}
    	}
    }

    至此,主界面也有了,接着我们需进行第三步,实现数据库与主界面之间的连接及信息的交流。

    三、数据库与可视化界面之间联系的建立
        在与数据库连接之前,我们需要进入到Windows的配置中进行数据源的配置,现在,我们开始

              1、打开“控制面板”,进入到“管理工具”,能够看到”ODBC数据源(64位)“左键双击击,接着在弹出的”ODBC数据源管理程序(64位)“对话框中选择“系统DSN”标签,选择其下的“添加”按钮,然后在弹出的对话框“创建数据源”中选择"Microsoft Access Driver"点击完成按钮后,又弹出一个绑定数据库对话框,即“ODBC Microsoft Access安装”,在这里需要输入数据源名,这儿我们取做"DDEmployer",点击选择按钮(如图3.1.1所示),进入文件夹选择第一步做好的数据库文件。最后配置的结果如图所示(图3.1.2)


    图3.1.1 命名数据源和选择数据库文件

    最终配置结果:

    最终配置结果

    图3.1.2 最终配置结果

    对于其他数据库的配置,我们能够参见如下链接处地址:部分数据库数据源的配置

             2、在配置完之后,我们进行程序代码的访问工作,打开eclipse,进行代码的编写,首先我们为按钮事件的响应创建四个类,类的名称分别为Delete、Insert、Update、Query这四个类,在建立好类之后,我们进行对类代码的编写,先以Insert为例,进行代码的编写,如下所示(图3.1.2为创建类Insert)


    图3.1.2 创建Insert类

    Insert代码:要注意的是,在代码中出现了关于NUMBER的错误,因为NUMBER为数据库的系统表示符,所以在使用时将其改为"NUMBERANDPHONE",所以在创建数据库的时候,需要使用更改后的表示符好,即将”NUMBER“字段改为”NUMBERPHONE“字段,否则会出现Insert into语句错误的提示。

    package Java_Employer;
    import java.sql.Connection;
    import java.sql.Date;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.text.DateFormat;
    
    import javax.print.attribute.DateTimeSyntax;
    public class Insert{
    	{	
    try {
    	     String id=javax.swing.JOptionPane.showInputDialog(null,"员工编号");
    		 String name=javax.swing.JOptionPane.showInputDialog(null,"输入姓名");
    		 String sex=javax.swing.JOptionPane.showInputDialog(null,"输入性别");
    		 String number=javax.swing.JOptionPane.showInputDialog(null,"输入联系方式");
    		 String registerdate=javax.swing.JOptionPane.showInputDialog(null,"输入登记日期");
    		 String address=javax.swing.JOptionPane.showInputDialog(null,"输入家庭住址");
    				Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    				Connection conn= DriverManager.getConnection("jdbc:odbc:DDEmployer");
    				 Statement stat=conn.createStatement();
    				 String sql="insert into T_EMPLOYER(ID,NAME,SEX,NUMBERANDPHONE,REGISTERDATE,ADDRESS) VALUES(?,?,?,?,?,?)";				 
    				 PreparedStatement ps=conn.prepareStatement(sql);	 
    				 ps.setString(1,id);
    				 ps.setString(2, name);
    				 ps.setString(3, sex);
    				 ps.setString(4,(number));
    				 ps.setString(5,registerdate);
    				 ps.setString(6, address);
    				 int i=ps.executeUpdate();
    				 conn.commit();
    				 System.out.println("成功添加"+i+"行");
    				 stat.close();
    				 conn.close();
    			}
    			catch (Exception e1) {
    			e1.printStackTrace();
    				}
    }
    }
    在编写好插入语句后,我们需要进入到MainFrame.java中进行初始化Insert函数类,即在语句中插入如下语句(Insert insert=new Insert();)即可:

     

    if (command.equals("插入")) {
    			System.out.println("插入");
    			Insert insert=new Insert();
    		}
    现在我们进行演示,点击eclipse运行主框架程序,则会弹出填写内容框,如下所示,按照要求填错写内容即可:

    图3.1.3 写入员工信息

    在输入完信息后,如果写入成功,则会在在eclipse控制台出现如下字样:


    图3.1.4 插入结果显示信息

    则会在数据库中插入数据,如下图所示:


    图3.1.5 新填数据的显示结果

      现在,我们完成了插入操作的代码,下面分别编写Delete、Update、Query类的实现,以相同的方法建立类,然后敲入里边的代码,下面是Delete类的代码:

     

    package Java_Employer;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    
    public class Delete {
    	{
    		try {
    			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    			Connection conn = DriverManager.getConnection("jdbc:odbc:DDEmployer");
    			Statement stat = conn.createStatement();
    			String id = javax.swing.JOptionPane.showInputDialog(null,"请输入要删除的员工编号");
    			String sql = "DELETE FROM T_EMPLOYER WHERE ID =?";
    			PreparedStatement ps = conn.prepareStatement(sql);
    			ps.setInt(1, Integer.valueOf(id));
    			int i = ps.executeUpdate();// 之前已经给了sql字符串,所以executeUpdate是无参的。
    			System.out.println("成功删除" + i + "行");
    			stat.close();
    			conn.close();
    		} catch (Exception e1) {
    			e1.printStackTrace();
    		}
    	}
    }
    下面是Update类的实现:
    package Java_Employer;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    
    public class Update 
    {
    	{
    	try {
    		/**
    		 * 修改
    		 */
    			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    			Connection conn= DriverManager.getConnection("jdbc:odbc:DDEmployer");
    			 Statement stat=conn.createStatement();
    				String id = javax.swing.JOptionPane.showInputDialog(null,"请输入要修改的员工编号");
    				String sex= javax.swing.JOptionPane.showInputDialog(null,"请输入要修改的员工性别");
    				String numberandphone= javax.swing.JOptionPane.showInputDialog(null,"请输入要修改的员工的联系方式");
    				String address = javax.swing.JOptionPane.showInputDialog(null,"请输入要修改的员工家庭住址");
    				String sql="UPDATE T_EMPLOYER SET SEX =?,NUMBERANDPHONE=?,ADDRESS=? WHERE ID=?";
    				PreparedStatement ps = conn.prepareStatement(sql);
    				ps.setString(1,sex);
    				ps.setString(2,numberandphone);
    				ps.setString(3,address);
    				ps.setInt(4, Integer.valueOf(id));
    				int i = ps.executeUpdate();// 之前已经给了sql字符串,所以executeUpdate是无参的。
    				conn.commit();
    				System.out.println("成功修改" + i + "行");
    			    stat.close();
    			    conn.close();
    		}
    		catch (Exception e1) {
    			e1.printStackTrace();
    		 }
    	}
    
    }

    下面是Query类的实现:

    package Java_Employer;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class Query 
    {
    	{
    	try {
    		Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    		Connection conn= DriverManager.getConnection("jdbc:odbc:DDEmployer");
    		 Statement stat=conn.createStatement();
    			String id = javax.swing.JOptionPane.showInputDialog(null,"请输入要修改的员工编号");
    			String sql="select * from T_EMPLOYER where id=?";
    			PreparedStatement ps = conn.prepareStatement(sql);
    			ps.setInt(1, Integer.valueOf(id));
    			ResultSet rs = ps.executeQuery();// 之前已经给了sql字符串,所以executeUpdate是无参的。
    			while(rs.next())
    			{
    		         System.out.println(rs.getString("ID")+"\t"+rs.getString("NAME")+"\t"+rs.getString("SEX")+"\t"+
    			rs.getString("NUMBERANDPHONe")+"\t"+rs.getString("ADDRESS"));
    			}
    		 stat.close();
    		 conn.close();
    	}
    	catch (Exception e1) {
    		e1.printStackTrace();
    	 };
    }
    
    }
    

    如下图所示为测试过程


    图 3.1.6 查询测试过程

    最后,只有知道了数据库里的数据,我们才能够操作,因此我们在创建一个类Flashface,创建过程与上面类的创建过程类似,现将代码贴出如下,需要注意的是需要在MainFrame类中进行初始化,即在mainFrame中加入语句“Flashface flashface=new Flashface();//显示数据库中的信息”

    package Java_Employer;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.Statement;
    public class Flashface {
    	{
        try {
    			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    			Connection conn = DriverManager.getConnection("jdbc:odbc:DDEmployer");
    			Statement stat = conn.createStatement();
    			String sql = "select * from T_EMPLOYER";
    			PreparedStatement ps = conn.prepareStatement(sql);
    			ResultSet rs = ps.executeQuery();// 之前已经给了sql字符串,所以executeUpdate是无参的。
    			while (rs.next()) {
    		System.out.println(rs.getString("ID") + "\t"+ rs.getString("NAME") + "\t" + rs.getString("SEX")
    						+ "\t" + rs.getString("NUMBERANDPHONe") + "\t"+ rs.getString("ADDRESS"));
    			}
    			stat.close();
    			conn.close();
    		} catch (Exception e1) {
    			e1.printStackTrace();
    		}
    	}
    }

    在编写完上面代码之后,运行能够看到在控制台显示数据库里的信息,现在,我们已经实现了开始时提出要求的功能,为了更容易使用,我们将数据显示到界面上,只需将MainFrame中的代码作调整即可

    :也就是在MainFrame类中增加一个函数show_table,详情请看下面代码:

    	/**
    	 * 显示table内容
    	 */
    	public static void show_table()
    	{
    		String str=null;
            StringBuffer strb=new StringBuffer();
    	    try {
    				Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    				Connection conn = DriverManager.getConnection("jdbc:odbc:DDEmployer");
    				Statement stat = conn.createStatement();
    				String sql = "select * from T_EMPLOYER";
    				PreparedStatement ps = conn.prepareStatement(sql);
    				ResultSet rs = ps.executeQuery();// 之前已经给了sql字符串,所以executeQuery是无参的。
    				while (rs.next()) 
    				    {
    			        /*System.out.println(rs.getString("ID") + "\t"+ rs.getString("NAME") + "\t" + rs.getString("SEX")
    							+ "\t" + rs.getString("NUMBERANDPHONE") + "\t"+rs.getString("REGISTERDATE")+"\t"+ 
    							rs.getString("ADDRESS"));*/
    			        str=str+(rs.getString("ID") + "\t"+ rs.getString("NAME") + "\t" + rs.getString("SEX")
    							+ "\t" + rs.getString("NUMBERANDPHONE") + "\t"+rs.getString("REGISTERDATE")+"\t"+ 
    							rs.getString("ADDRESS")+"\n");
    			        
    					 }
    				 stat.close();
    	    		 conn.close();
    			} 
    	        catch (Exception e1) 
    			{
    				e1.printStackTrace();
    			}
    	    jTextArea_list.setText(str);
    		
    	}

    增加这个函数后就能够及时的查看数据库里的内容。

    下面是程于最终的运行界面:


    哈哈哈,到此,我们已经完成了全部工作,当然含有很多缺点在程序中,比如输入对话框弹出式,假如用户不输入任何信息时,插入时员工编号重叠时,查找时没有要找的信息等这些情况,都没具有相应的处理程序,希望读者能够注意到,有兴趣的可以继续修改。谢谢。










    展开全文
  • 如何通过Spring Boot配置动态数据源访问多个数据库

    万次阅读 多人点赞 2018-03-18 14:59:52
    之前写过一篇博客《Spring+Mybatis+Mysql搭建分布式数据库访问框架》描述如何通过Spring+Mybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况。...

    之前写过一篇博客《Spring+Mybatis+Mysql搭建分布式数据库访问框架》描述如何通过Spring+Mybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况。针对数据库动态增加的情况无能为力。

    下面讲的方案能支持数据库动态增删,数量不限。

    一、数据库环境准备

    下面以Mysql为例,先在本地建3个数据库用于测试。需要说明的是本方案不限数据库数量,支持不同的数据库部署在不同的服务器上。如图所示db_project_001、db_project_002、db_project_003。
    这里写图片描述

    #二、搭建Java后台微服务项目
    创建一个Spring Boot的maven项目:
    这里写图片描述

    config:数据源配置。
    datasource:自己实现的动态数据源相关类。
    dbmgr:管理项目编码与数据库IP、名称的映射关系(实际项目中这部分数据保存在redis缓存中,可动态增删)。
    mapper:mybatis的数据库访问接口。
    model:映射模型。
    rest:微服务对外发布的restful接口,这里用来测试。
    application.yml:配置数据库JDBC参数。

    三、详细的代码实现

    1、数据源配置管理类(DataSourceConfig.java)

    package com.elon.dds.config;
    
    import javax.sql.DataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.elon.dds.datasource.DynamicDataSource;
    
    /**
     * 数据源配置管理。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @Configuration
    @MapperScan(basePackages="com.elon.dds.mapper", value="sqlSessionFactory")
    public class DataSourceConfig {
    
    	/**
    	 * 根据配置参数创建数据源。使用派生的子类。
    	 * 
    	 * @return 数据源
    	 */
    	@Bean(name="dataSource")
    	@ConfigurationProperties(prefix="spring.datasource")
    	public DataSource getDataSource() {
    		DataSourceBuilder builder = DataSourceBuilder.create();
    		builder.type(DynamicDataSource.class);
    		return builder.build();
    	}
    	
    	/**
    	 * 创建会话工厂。
    	 * 
    	 * @param dataSource 数据源
    	 * @return 会话工厂
    	 */
    	@Bean(name="sqlSessionFactory")
    	public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
    		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    		bean.setDataSource(dataSource);
    		
    		try {
    			return bean.getObject();
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    }
    
    

    2、定义动态数据源

    (1)首先增加一个数据库标识类,用于区分不同的数据库(DBIdentifier.java)

    由于我们为不同的project创建了单独的数据库,所以使用项目编码作为数据库的索引。而微服务支持多线程并发的,采用线程变量。

    package com.elon.dds.datasource;
    
    /**
     * 数据库标识管理类。用于区分数据源连接的不同数据库。
     * 
     * @author elon
     * @version 2018-02-25
     */
    public class DBIdentifier {
    	
    	/**
    	 * 用不同的工程编码来区分数据库
    	 */
    	private static ThreadLocal<String> projectCode = new ThreadLocal<String>();
    
    	public static String getProjectCode() {
    		return projectCode.get();
    	}
    
    	public static void setProjectCode(String code) {
    		projectCode.set(code);
    	}
    }
    
    

    (2)从DataSource派生了一个DynamicDataSource,在其中实现数据库连接的动态切换(DynamicDataSource.java)

    package com.elon.dds.datasource;
    
    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.tomcat.jdbc.pool.DataSource;
    import org.apache.tomcat.jdbc.pool.PoolProperties;
    
    import com.elon.dds.dbmgr.ProjectDBMgr;
    
    /**
     * 定义动态数据源派生类。从基础的DataSource派生,动态性自己实现。
     * 
     * @author elon
     * @version 2018-02-25
     */
    public class DynamicDataSource extends DataSource {
    	
    	private static Logger log = LogManager.getLogger(DynamicDataSource.class);
    	
    	/**
    	 * 改写本方法是为了在请求不同工程的数据时去连接不同的数据库。
    	 */
    	@Override
    	public Connection getConnection(){
    		
    		String projectCode = DBIdentifier.getProjectCode();
    		
    		//1、获取数据源
    		DataSource dds = DDSHolder.instance().getDDS(projectCode);
    		
    		//2、如果数据源不存在则创建
    		if (dds == null) {
    			try {
    				DataSource newDDS = initDDS(projectCode);
    				DDSHolder.instance().addDDS(projectCode, newDDS);
    			} catch (IllegalArgumentException | IllegalAccessException e) {
    				log.error("Init data source fail. projectCode:" + projectCode);
    				return null;
    			}
    		}
    		
    		dds = DDSHolder.instance().getDDS(projectCode);
    		try {
    			return dds.getConnection();
    		} catch (SQLException e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    	
    	/**
    	 * 以当前数据对象作为模板复制一份。
    	 * 
    	 * @return dds
    	 * @throws IllegalAccessException 
    	 * @throws IllegalArgumentException 
    	 */
    	private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
    		
    		DataSource dds = new DataSource();
    		
    		// 2、复制PoolConfiguration的属性
    		PoolProperties property = new PoolProperties();
    		Field[] pfields = PoolProperties.class.getDeclaredFields();
    		for (Field f : pfields) {
    			f.setAccessible(true);
    			Object value = f.get(this.getPoolProperties());
    			
    			try
    			{
    				f.set(property, value);				
    			}
    			catch (Exception e)
    			{
    				//有一些static final的属性不能修改。忽略。
    				log.info("Set value fail. attr name:" + f.getName());
    				continue;
    			}
    		}
    		dds.setPoolProperties(property);
    
    		// 3、设置数据库名称和IP(一般来说,端口和用户名、密码都是统一固定的)
    		String urlFormat = this.getUrl();
    		String url = String.format(urlFormat, ProjectDBMgr.instance().getDBIP(projectCode), 
    				ProjectDBMgr.instance().getDBName(projectCode));
    		dds.setUrl(url);
    
    		return dds;
    	}
    }
    
    

    (3)通过DDSTimer控制数据连接释放(DDSTimer.java)

    package com.elon.dds.datasource;
    
    import org.apache.tomcat.jdbc.pool.DataSource;
    
    /**
     * 动态数据源定时器管理。长时间无访问的数据库连接关闭。
     * 
     * @author elon
     * @version 2018年2月25日
     */
    public class DDSTimer {
    	
    	/**
    	 * 空闲时间周期。超过这个时长没有访问的数据库连接将被释放。默认为10分钟。
    	 */
    	private static long idlePeriodTime = 10 * 60 * 1000;
    	
    	/**
    	 * 动态数据源
    	 */
    	private DataSource dds;
    	
    	/**
    	 * 上一次访问的时间
    	 */
    	private long lastUseTime;
    	
    	public DDSTimer(DataSource dds) {
    		this.dds = dds;
    		this.lastUseTime = System.currentTimeMillis();
    	}
    	
    	/**
    	 * 更新最近访问时间
    	 */
    	public void refreshTime() {
    		lastUseTime = System.currentTimeMillis();
    	}
    	
    	/**
    	 * 检测数据连接是否超时关闭。
    	 * 
    	 * @return true-已超时关闭; false-未超时
    	 */
    	public boolean checkAndClose() {
    		
    		if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
    		{
    			dds.close();
    			return true;
    		}
    		
    		return false;
    	}
    
    	public DataSource getDds() {
    		return dds;
    	}
    }
    
    

    (4)通过DDSHolder来管理不同的数据源,提供数据源的添加、查询功能(DDSHolder.java)

    package com.elon.dds.datasource;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Timer;
    
    import org.apache.tomcat.jdbc.pool.DataSource;
    
    /**
     * 动态数据源管理器。
     * 
     * @author elon
     * @version 2018年2月25日
     */
    public class DDSHolder {
    	
    	/**
    	 * 管理动态数据源列表。<工程编码,数据源>
    	 */
    	private Map<String, DDSTimer> ddsMap = new HashMap<String, DDSTimer>();
    
    	/**
    	 * 通过定时任务周期性清除不使用的数据源
    	 */
    	private static Timer clearIdleTask = new Timer();
    	static {
    		clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
    	};
    	
    	private DDSHolder() {
    		
    	}
    	
    	/*
    	 * 获取单例对象
    	 */
    	public static DDSHolder instance() {
    		return DDSHolderBuilder.instance;
    	}
    	
    	/**
    	 * 添加动态数据源。
    	 * 
    	 * @param projectCode 项目编码 
    	 * @param dds dds
    	 */
    	public synchronized void addDDS(String projectCode, DataSource dds) {
    		
    		DDSTimer ddst = new DDSTimer(dds);
    		ddsMap.put(projectCode, ddst);
    	}
    	
    	/**
    	 * 查询动态数据源
    	 * 
    	 * @param projectCode 项目编码
    	 * @return dds
    	 */
    	public synchronized DataSource getDDS(String projectCode) {
    		
    		if (ddsMap.containsKey(projectCode)) {
    			DDSTimer ddst = ddsMap.get(projectCode);
    			ddst.refreshTime();
    			return ddst.getDds();
    		}
    		
    		return null;
    	}
    	
    	/**
    	 * 清除超时无人使用的数据源。
    	 */
    	public synchronized void clearIdleDDS() {
    		
    		Iterator<Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
    		for (; iter.hasNext(); ) {
    			
    			Entry<String, DDSTimer> entry = iter.next();
    			if (entry.getValue().checkAndClose())
    			{
    				iter.remove();
    			}
    		}
    	}
    	
    	/**
    	 * 单例构件类
    	 * @author elon
    	 * @version 2018年2月26日
    	 */
    	private static class DDSHolderBuilder {
    		private static DDSHolder instance = new DDSHolder();
    	}
    }
    
    

    (5)定时器任务ClearIdleTimerTask用于定时清除空闲的数据源(ClearIdleTimerTask.java)

    package com.elon.dds.datasource;
    
    import java.util.TimerTask;
    
    /**
     * 清除空闲连接任务。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    public class ClearIdleTimerTask extends TimerTask {
    	
    	@Override
    	public void run() {
    		DDSHolder.instance().clearIdleDDS();
    	}
    }
    
    

    (6)管理项目编码与数据库IP和名称的映射关系(ProjectDBMgr.java)

    package com.elon.dds.dbmgr;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 项目数据库管理。提供根据项目编码查询数据库名称和IP的接口。
     * @author elon
     * @version 2018年2月25日
     */
    public class ProjectDBMgr {
    	
    	/**
    	 * 保存项目编码与数据名称的映射关系。这里是硬编码,实际开发中这个关系数据可以保存到redis缓存中;
    	 * 新增一个项目或者删除一个项目只需要更新缓存。到时这个类的接口只需要修改为从缓存拿数据。
    	 */
    	private Map<String, String> dbNameMap = new HashMap<String, String>();
    	
    	/**
    	 * 保存项目编码与数据库IP的映射关系。
    	 */
    	private Map<String, String> dbIPMap = new HashMap<String, String>();
    	
    	private ProjectDBMgr() {
    		dbNameMap.put("project_001", "db_project_001");
    		dbNameMap.put("project_002", "db_project_002");
    		dbNameMap.put("project_003", "db_project_003");
    		
    		dbIPMap.put("project_001", "127.0.0.1");
    		dbIPMap.put("project_002", "127.0.0.1");
    		dbIPMap.put("project_003", "127.0.0.1");
    	}
    	
    	public static ProjectDBMgr instance() {
    		return ProjectDBMgrBuilder.instance;
    	}
    	
    	// 实际开发中改为从缓存获取
    	public String getDBName(String projectCode) {
    		if (dbNameMap.containsKey(projectCode)) {
    			return dbNameMap.get(projectCode);
    		}
    		
    		return "";
    	}
    	
    	//实际开发中改为从缓存中获取
    	public String getDBIP(String projectCode) {
    		if (dbIPMap.containsKey(projectCode)) {
    			return dbIPMap.get(projectCode);
    		}
    		
    		return "";
    	}
    	
    	private static class ProjectDBMgrBuilder {
    		private static ProjectDBMgr instance = new ProjectDBMgr();
    	}
    }
    
    

    (7)编写数据库访问的mapper(UserMapper.java)

    package com.elon.dds.mapper;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;
    
    import com.elon.dds.model.User;
    
    /**
     * Mybatis映射接口定义。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @Mapper
    public interface UserMapper
    {
    	/**
    	 * 查询所有用户数据
    	 * @return 用户数据列表
    	 */
    	@Results(value= {
    			@Result(property="userId", column="id"),
    			@Result(property="name", column="name"),
    			@Result(property="age", column="age")
    	})
    	@Select("select id, name, age from tbl_user")
    	List<User> getUsers();
    } 
    
    

    (8)定义查询对象模型(User.java)

    package com.elon.dds.model;
    
    public class User
    {
    	private int userId = -1;
    
    	private String name = "";
    	
    	private int age = -1;
    	
    	@Override
    	public String toString()
    	{
    		return "name:" + name + "|age:" + age;
    	}
    
    	public int getUserId()
    	{
    		return userId;
    	}
    
    	public void setUserId(int userId)
    	{
    		this.userId = userId;
    	}
    
    	public String getName()
    	{
    		return name;
    	}
    
    	public void setName(String name)
    	{
    		this.name = name;
    	}
    
    	public int getAge()
    	{
    		return age;
    	}
    
    	public void setAge(int age)
    	{
    		this.age = age;
    	}
    }
    
    

    (9)定义查询数据的restful接口(WSUser.java)

    package com.elon.dds.rest;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.elon.dds.datasource.DBIdentifier;
    import com.elon.dds.mapper.UserMapper;
    import com.elon.dds.model.User;
    
    /**
     * 用户数据访问接口。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @RestController
    @RequestMapping(value="/user")
    public class WSUser {
    
    	@Autowired
    	private UserMapper userMapper;
    	
    	/**
    	 * 查询项目中所有用户信息
    	 * 
    	 * @param projectCode 项目编码
    	 * @return 用户列表
    	 */
    	@RequestMapping(value="/v1/users", method=RequestMethod.GET)
    	public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode) 
    	{
    		DBIdentifier.setProjectCode(projectCode);
    		return userMapper.getUsers();
    	}
    }
    
    

    要求每次查询都要带上projectCode参数。

    (10)编写Spring Boot App的启动代码(App.java)

    package com.elon.dds;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * Hello world!
     *
     */
    @SpringBootApplication
    public class App 
    {
        public static void main( String[] args )
        {
            System.out.println( "Hello World!" );
            SpringApplication.run(App.class, args);
        }
    }
    
    

    四、在application.yml中配置数据源

    其中的数据库IP和数据库名称使用%s。在执行数据操作时动态切换。

    spring:
     datasource:
      url: jdbc:mysql://%s:3306/%s?useUnicode=true&characterEncoding=utf-8
      username: root
      password: 
      driver-class-name: com.mysql.jdbc.Driver
    
    logging:
     config: classpath:log4j2.xml
    

    五、测试方案

    1、查询project_001的数据,正常返回

    这里写图片描述

    2、查询project_002的数据,正常返回

    这里写图片描述

    代码在github上的路径:https://github.com/ylforever/elon-dynamicdatasource

    展开全文
  • 网上问的人也很实现方法大体类似,本人将实际过程中的实现简化,希望能抛砖引玉。 利用Hibernate访问不同数据库中的不同表或不同数据库中的相同表。 本人在开发过程中的解决方案,希望大家交流。 一般用...

    网上问的人也很多,实现方法大体类似,本人将实际过程中的实现简化,希望能抛砖引玉。


    利用Hibernate访问不同数据库中的不同表或不同数据库中的相同表。

    本人在开发过程中的解决方案,希望大家交流。
    一般用myEclipse工具会自动生成Hibernate的相关文件,大致有下面几类:

    1)数据库配置文件:.cfg.xml
    2)映射文件.hbm.xml
    3)映射类:pojo
    4)会话工厂类:SessionFactory
    5)基础DAO接口:IBaseHibernateDAO
    6DAO接口的实现基础类:BaseHibernateDAO
    7)数据访问对象:DAO。所有DAO默认继承BaseHibernateDAO

    当然,不同的工具生成的文件也略有差异,但不影响实现思路。


    一般工具生成的配置只针对一个数据库,所以都生成了一个数据库配置文件。
    下面我以访问不同数据库中相同的表做例子,访问不同数据库的不同表原理类同。
    假设有两个数据库db1 db2,两个库中都有user,要实现对两个数据库中的表访问,操作如下:

    1)配置两个数据库配置文件db1.cfg.xmldb2.cfg.xml ,分别连接两个数据库。
    2)配置两个SessionFactorySessionFactory_db1绑定db1.cfg.xmlSessionFactory_db2 ,绑定db2.cfg.xml
    3)基础DAO接口IBaseHibernateDAO保持不变,代码默认如下
    public interface IBaseHibernateDAO {
    public Session getSession( );

    }

    4DAO接口的实现基础类BaseHibernateDAO改造后如下:


    public class BaseHibernateDAO implementsIBaseHibernateDAO
     {

    private String dbName;//要连接的数据库

    // 为了保证每个DAO能够正确指定所操作的数据库,将无参构造设为私有 
    private BaseHibernateDAO() { }

    // 指定目标数据库的DAO构造方法 
    public BaseHibernateDAO(StringdbName) {
       this.dbName = dbName;

    }

    //重写getSession()方法,使之能够访问不同的数据库
    public Session getSession() {
       if (dbName == null) {
        return null;
       }else if (dbName.equals("db01
    ")){   
       returnSessionFactory_db01.getSession();//连接db01
       } else if (dbName.equals("db02")) {   
       returnSessionFactory_db02.getSession();//连接db02
       } else {
        return null;
       }
    }
    }

    5)改造自动生成用户表对应的DAO——UserDAO :即继承父类BaseHibernateDAO的有参构造。

    public class UserDAOextends BaseHibernateDAO {

       //因为父类将无参构造设置为了private,所以该类只能存在有参构造了。
        public UserInfoTbDAO(StringdbName) {

           super(dbName);  
        }

        //以下是自动生成的代码:
        public void save(UserTb transientInstance){        
            try {
               getSession().save(transientInstance);
               log.debug("save successful");
            } catch (RuntimeException re) {
               log.error("save failed", re);
                throw re;
            }
        }
    }

    6)应用层的测试

    public voidtestSaveUser(UserTb user){

    UserDAO userDao_1 = newUserDAO("db01");//得到db01库的DAO
    UserDAO userDao_2 = new UserDAO("db02");//
    得到db02库的DAO

    //假设两个库中的数据要同步更新(实际操作中应该加入事务控制)
    userDao_1.save();//
    更新01
    userDao_2.save();//
    更新02

    }

    7)总结:
    优点:

    1.
    代码改动比较小,能充分利用工具生成的代码。
    2.
    结构简单,访问灵活。
    3.
    访问不同库的同一个表,只需一个DAO,一个pojo,无需写额外代码。

    缺点:
    1.
    每次连接数据库时都要指定访问的数据库。
    2.
    为了有效指定数据库逻辑名,和利于维护,就得“db01”这样的字符串设置为全局的final变量,或者弄

    一个DAO工厂来产生不同的DAO实例。

    另一种方法:
    上面的方法是改造了DAO的构造方法。也可以改造getSession()方法,这样透明度更高,但略欠灵活。

    本人刚刚接触Hibernate,不知以上设计是否妥当,望高手指点一二。

     


    展开全文
  • 当然,数据库也是要分开的。如果能用phpmyadmin直接访问两台服务器上的mysql就好了。这就是需求。二、解决方案1、找到phpmyadmin文件夹下面的config.sample.inc.php,重命名为config.inc.php。2、打开config.inc.php...
  • 初学者一定要做的便是用户的注册与登录,同时牵涉到数据库的相关操作,可以利用Navicat建立一简单的数据库,在里面更新、插入一些数据。之后写python文件对数据库进行操作,我们可以写一MysqlHelp.py文件帮助...
  • 本文使用springboot+mybatis+SpringSecurity 实现用户权限数据库管理实现用户和角色用数据库存储,而资源(url)和权限的对应采用硬编码配置。 也就是角色可以访问的权限通过硬编码控制。角色和用户的关系通过数据库...
  • 本文将为大家介绍如何创建一个简单的存储过程来实现同时终止多个会话、结束连续的会话和结束连接到数据库的所有会话等功能。  在很多情况下,往往会要求数据库管理员终止SQL Server中的用户进程,例如在...
  • 实现微型数据库

    千次阅读 2014-07-22 16:55:16
    实现微型数据库的基本原理
  • 默认的情况下,oracle里面的用户A,要访问用户B的表需要带用户B的前缀,如访问用户B的 user表,需要这样访问 select * from B.user;如果想要不添加用户前缀,需要这样处理: 1.用视图 create view user select * ...
  • Java小程序之集合框架模拟数据库实现用户登录和注册功能 前言:关于Java中的集合框架知识,我以前也写过一篇博客,大家可以去查阅;通过再次学习java中的集合框架知识,我又有了新的理解,更能从整体上认知Java中...
  • 首先创建一空的MVC项目,然后把三层给搭起来
  • Java Web中如何访问数据库

    千次阅读 2016-05-14 11:16:38
    对于数据库访问,有很方式,这里我们进行实现简单的示例访问,使用DAO和VO进行访问,下面是示例代码 首先,我们进行建立一JavaBean类,命名为Student.java类,具体代码如下所示,其中注释如下: package ...
  • 数据库访问方式

    万次阅读 2010-10-18 18:26:00
    数据库访问方式
  • java数据库连接池实现原理

    万次阅读 多人点赞 2014-05-10 10:53:53
     数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一物理数据库连接,每次操作都打开一物理连接,使用完都关闭连接,这样造成系统的 性能...
  • 基于数据库实现的分布式锁

    万次阅读 多人点赞 2019-06-20 00:43:21
    在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行...
  • 本人最近完成了一封装数据库访问抽象层的项目。我们开发的数据库访问抽象层作为分布式集群基础平台的一组件。可以支持不同数据库编程接口(OCI、mysql、ODBC、pgsql)等。本系列博客主要分享一下我在开发数据库...
  • 本例采用mysql数据库,因此请先下载mysql-connection.jar ...事实上,同一时间需要创建数据库连接的请求数量并不,一般几百足够了。那么我们可以根据需要创建一连接池,它负责分配、管理和释放数据库连接,它允许
  • 首先我们要明确:表名和用户没有关系,关键的一个实例下,不... 通常情况我们都是在一个数据库实例下创建多个用户,因此就会出现不同用户下表名重复时,hibernate映射表映射不出来的问题,解决办法就是在访问数据库表时
  • Python 访问数据库

    千次阅读 2018-06-29 08:19:55
    文章有些代码图片借鉴与其他文章, 希望原创... 这链接点击打开链接 https://wiki.python.org/moin/DatabaseInterfaces是python 关于数据库接口的一总结 , 可以看到python支持的访问数据库系统。2.模块:...
  • 正如我上一篇博文提到,ADO这货和MFC没有任何关系,ADO 是一独立的组件。所以为了使用ADO wi
  • Java web项目中,无论项目是大是小,或多或少都会涉及到用户访问权限的控制,权限管理总体的设计思路就是,不该看的不看,不该做的不做!据我目前的了解,我所知道的几种实现访问权限控制的方式有: JQuery的zTree...
  • 它实际上定义了一组标准的数据库的接口,为了实现通过java操作数据库,必须实现这些接口,不同的数据库厂商都提供了对JDBC接口的实现,这些具体的实现被打包成一jar包(也就是数据库驱动),供我们在开发的时候...
  • 数据库分库分表策略的具体实现方案

    万次阅读 多人点赞 2017-01-02 14:10:03
    1、 使用Spring AOP实现MySQL数据库读写分离案例分析 2、MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解 3、MySQL主从复制的常见拓扑、原理分析以及如何提高主从复制的效率总结 4、使用mysqlreplicate...
  • django官方文档——使用多个数据库

    千次阅读 2012-08-31 11:47:06
    使用多个数据库¶ New in Django 1.2: Please, see the release notes 大多数其他文档都假设使用单一数据库,本文主要讨论如何在 Django 中使用多个数据库。使用多个数据库,要增加一些步骤。 定义你的数据库...
  • 3)点击权限,选择添加权限,出现MySQL中已存在的数据库列表,选择你要为该新建用户开放的数据库,此处选择“maibao”数据库,选择一些必要的权限信息(我这里进行了全部权限选择),确定 4)查看新建用户...
  • 是一款很程序员都喜爱的数据库,由于呢 MYSQL 是一开源的,带一点半商业的,市场的占有率比较高,所以一直以来都被认为是 PHP 的最佳搭档,同时 PHP 也具有很强大的数据库支持能力,本篇主要讲解 PHP访问MySQL...
  • 数据库用户数据库角色

    千次阅读 2006-05-04 21:54:00
    在一个数据库中,用户ID 惟一标识一个用户,用户对数据的访问权限以及对数据库对象的所有关系都是通过用户账号来控制的,用户账号总是基于数据库的,即两个不同数据库中可以有两个相同的用户账号。 在数据库中,...
  • Unity C# 连接SQL Server数据库实现获取和添加登录注册的用户列表。 参考:C#操作SQL Server数据库 Github:C#操作SQL Server练习 Github:MyGameServer(服务器、连接数据库) Github:PhotonChatRoom...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 704,676
精华内容 281,870
关键字:

数据库实现多个用户同时访问