zoukankan      html  css  js  c++  java
  • 第二十部分_Hibernate原理、API与设计详解、Struts2.1整合Hibernate

    具体操作步骤:

    新建一个WebProject,命名为hibernate,在tomcat安装目录下的conf目录下的server.xml文件中引入其context path(在</host>之前):<Context path="/hibernate" docBase="D:JavaWebhibernateWebRoot" reloadable="true"/> 添加struts依赖的jar包和mysql的驱动一共七个jar文件(commons-fileupload-1.2.1.jar、commons-logging-1.0.4.jar、freemarker-2.3.13.jar、ognl-2.6.11.jar、struts2-core-2.1.6.jar、xwork-core-2.1.2.jar、mysql-connector-java-5.1.34-bin.jar),粘贴到WEB-INF的lib目录下。

    选中项目,点击菜单栏的MyEclipse(或右键MyEclipse),选择Project Capabilities->Add Hibernate Capabilities。JAR Library Installation选中Copy checked Library Jars to project folder and add to build-path,为了方便,这里Hibernate规范与风中叶老师的规范一致选择3.2,点击next,弹出Create Hibernate XML configuration file对话框继续next,在Specify Hibernate database connection details对话框中去掉Specify database connection details前面的√,点击next,去掉Create SessionFactory class?前面的√,点击Finish。这样我们的项目依赖的hibernate相关的jar文件就被纳入到项目下面了。

    hibernate项目下的src目录下自动生成了一个文件:hibernate.cfg.xml,切换到source视图。 在<session-factory></session-factory>之间添加如下信息:

    <?xml version='1.0' encoding='UTF-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
              "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
              "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    
    <!-- Generated by MyEclipse Hibernate Tools.                   -->
    <hibernate-configuration>
    
        <session-factory>
        	<property name="show_sql">true</property> <!-- 属性之间没有上下关系,放在哪里都行 -->
        	
        	<property name="connection.url">jdbc:mysql://localhost:3306/myhibernate</property>
        	<property name="connection.username">root</property>
        	<property name="connection.password">root</property>
        	<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        	
        	<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
       
       		<mapping resource="Person.hbm.xml"/> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 -->
       
        </session-factory>
    
    </hibernate-configuration>
    

    由于hibernate可以支持多种数据库,所以其dialect(方言)属性必须配置,可以在Hibernate3.2API文档中找到org.hibernate.dialect这个包下的MySqlDialect类。

    接着在MySql中创建数据库Hibernate,新建表:person,字段有:id(主键,int,长度11,非空,注意:不将id设置为自动增加类型,让hibernate为我们做这些,体会hibernate的作用)、username(varchar,长度20,允许空值)、password(varchar,长度20,允许空值)、age(int,长度11,允许空值)、registerdate(datetime,长度0,允许空值)

    接着对这张表创建相应的类,即POJO,新建包com.hibernate.model,在该包下建立相应的持久化类:Person.java

    package com.hibernate.model;
    
    import java.sql.Date;
    
    public class Person
    {
    	private Integer id; // int也没有问题,但是最好使用其包装类Integer
    	
    	private String username;
    	
    	private String password;
    	
    	private Integer age;
    	
    	private Date registerdate;
    
    	public Integer getId()
    	{
    		return id;
    	}
    
    	public void setId(Integer id)
    	{
    		this.id = id;
    	}
    
    	public String getUsername()
    	{
    		return username;
    	}
    
    	public void setUsername(String username)
    	{
    		this.username = username;
    	}
    
    	public String getPassword()
    	{
    		return password;
    	}
    
    	public void setPassword(String password)
    	{
    		this.password = password;
    	}
    
    	public Integer getAge()
    	{
    		return age;
    	}
    
    	public void setAge(Integer age)
    	{
    		this.age = age;
    	}
    
    	public Date getRegisterdate()
    	{
    		return registerdate;
    	}
    
    	public void setRegisterdate(Date registerdate)
    	{
    		this.registerdate = registerdate;
    	}
    	
    	
    }
    

    然后在src目录下对Person.java文件创建一个Hibernate映射文件Person.hbm.xml(hibernate mapping),可将其选中,右键选择Open With->MyEclipse Hibernate Mapping Editor,这样写xml的时候就带提示了。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping>
    
    	<class name="com.hibernate.model.Person" table="person"> <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 -->
    		
    		<id name="id" column="id" type="int"> <!-- 类中id属性和映射到表中的id字段,类型为int/integer皆可 -->
    			<generator class="increment"> <!-- 主键id的生成方式为自增 -->
    			</generator>
    		</id>
    	
    		<property name="username" column="username" type="string"></property> <!-- 如果不写字段名,则默认与类中的属性名相同 -->
    		<property name="password" column="password" type="string"></property>
    		<property name="age" column="age" type="int"></property>
    		<property name="registerdate" column="registerdate" type="date"></property>
    
    	</class>
    
    </hibernate-mapping>
    

    这样一个类-一张表-一个hbm已经全部创建完成。这个hbm就起到连接类和表的作用

    然后,新建包com.hibernate.util,在下面新建类:HibernateUtil.java(可以不写,但是写上的话可以方便我们对后台数据库进行操作):

    package com.hibernate.util;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    public class HibernateUtil
    {
    	private static SessionFactory sessionFactory;
    	
    	static
    	{
    		try
    		{
    			sessionFactory = new Configuration().configure().buildSessionFactory();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("构造SessionFactory异常发生:" + ex.getMessage());
    		}
    		
    	}
    	
    	public static Session currentSession()
    	{
    		Session session = sessionFactory.openSession();
    		
    		return session;
    	}
    	
    	public static void closeSession(Session session)
    	{
    		if(null != session)
    		{
    			session.close();
    		}
    	}
    }
    

    接下来,创建com.hibernate.persister包,在该包下面创建类DBPreson.java,完成增删查改操作:

    package com.hibernate.persistence;
    
    import java.util.List;
    
    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.hibernate.model.Person;
    import com.hibernate.util.HibernateUtil;
    
    public class DBPerson
    {
    	/**
    	 * 创建新的用户
    	 */
    	
    	public static void save(Person person)
    	{
    		Session session = HibernateUtil.currentSession();
    		
    		Transaction tx = session.beginTransaction(); // 开启事务
    		
    		try
    		{	
    			session.save(person);
    			tx.commit();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("增加用户异常发生!");
    			if(null != tx)
    			{
    				tx.rollback();
    			}
    		}
    		finally
    		{
    			HibernateUtil.closeSession(session);
    		}
    		
    	}
    	
    	/**
    	 * 查询出所有用户
    	 */
    	
    	@SuppressWarnings("unchecked")
    	public static List<Person> listAll()
    	{
    		Session session = HibernateUtil.currentSession();
    		
    		Transaction tx = session.beginTransaction(); // 开启事务
    		
    		List<Person> list = null;
    		
    		try
    		{
    			
    			Query query = session.createQuery("from Person"); // hql语句,Hibernate query language,这里Person是类名
    			
    			list = (List<Person>)query.list();
    			
    			tx.commit();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("增加用户异常发生!");
    			if(null != tx)
    			{
    				tx.rollback();
    			}
    		}
    		finally
    		{
    			HibernateUtil.closeSession(session);
    		}
    		
    		return list;
    	}
    }
    

    我们先不把DAO这块全部写完,因为现在主要关注的点是整个流程,因此我们先编写界面端,流程跑通了以后再返回来编写DAO。在WebRoot下面新建一个register.jsp:

     <body>
        <form action="save.action">
        
        username:<input type="text" name="username" size="20"><br>
        password:<input type="password" name="password" size="20"><br>
        age:<input type="text" name="age" size="20"><br>
        
        <input type="submit" value="submit">
        </form>
      </body>
    

    JSP登录页面编写完后,紧接着会点击提交,提交后流程来到struts.xml中:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
    	
    	<struts>
    		<package name="hibernate" extends="struts-default">
    		
    			<action name="save" class="com.test.action.PersonAction" method="save"> <!-- 这里关注点在hibernate,因此struts的输入校验就省略了 -->
    				<result name="success">/listAll.jsp</result>
    			</action>
    		</package>
    	</struts>
    

    接下来创建相应的action:PersonAction.java:

    package com.test.action;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.struts2.ServletActionContext;
    
    import com.hibernate.model.Person;
    import com.hibernate.persistence.DBPerson;
    import com.opensymphony.xwork2.ActionSupport;
    
    public class PersonAction extends ActionSupport
    {
    	private int id; // 因为增删查改共用一个action,所以这里增加不需要id,但是删除、查找、修改都需要提供一个id这样的熟悉,增加的时候没有id,只是不给它赋值而已,并不会产生什么问题
    	
    	private String username;
    	
    	private String password;
    	
    	private int age;
    
    	public int getId()
    	{
    		return id;
    	}
    
    	public void setId(int id)
    	{
    		this.id = id;
    	}
    
    	public String getUsername()
    	{
    		return username;
    	}
    
    	public void setUsername(String username)
    	{
    		this.username = username;
    	}
    
    	public String getPassword()
    	{
    		return password;
    	}
    
    	public void setPassword(String password)
    	{
    		this.password = password;
    	}
    
    	public int getAge()
    	{
    		return age;
    	}
    
    	public void setAge(int age)
    	{
    		this.age = age;
    	}
    	
    	// 完成用户增加的操作
    	public String save() throws Exception
    	{
    		Person person = new Person();
    		
    		if(username != null)
    			username = new String(username.getBytes("ISO-8859-1"),"utf-8"); // 解决中文乱码问题
    		
    		person.setUsername(username);
    		person.setPassword(password);
    		person.setAge(age);
    
    		java.sql.Date registerDate = new java.sql.Date(new java.util.Date().getTime());
    		person.setRegisterdate(registerDate);
    		
    		DBPerson.save(person); // 将person对象存到数据库中
    	
    		List<Person> list = DBPerson.listAll();
    		
    		HttpServletRequest request = ServletActionContext.getRequest();
    		
    		request.setAttribute("list", list);
    		
    		return SUCCESS;
    		
    	}
    	
    }
    

    接下来根据struts.xml的标识,流程应该转到listAll.jsp: 

    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
    <%@ taglib uri="/struts-tags" prefix="s" %>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'listAll.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    	<!--
    	<link rel="stylesheet" type="text/css" href="styles.css">
    	-->
    	
    	<script type="text/javascript">
    	
    	function del()
    	{
    		if(confirm("Are you sure?"))
    			return true;
    		return false;
    	}
    	
    	</script>
    	
      </head>
      
      <body>
       	<table width="80%" align="center" border="1">
       	
       		<tr>
       			<th> <%--<th>:定义表格内的表头单元格。此th元素内部的文本通常会呈现为粗体。--%> 
       				username
       			</th>
       	
       			<th>
       				password
       			</th>
       	
       			<th>
       				registerdate
       			</th>
       	
       			<th>
       				age
       			</th>
       	
       			<th>
       				update
       			</th>
       		
       			<th>
       				delete
       			</th>
       		</tr>
       		
       		<s:iterator value="#request.list" id="person"> <!-- 或者嵌入Java代码使用for循环的形式 -->
       		
       			<tr>
       			<td>
       				<s:a href="getPerson.action?id=%{#person.id}">
       				<s:property value="username"/> <!-- username是action中的一个成员变量 -->
       				</s:a>
       			</td>
       		
       			<td>
       				<s:property value="password"/>
       			</td>
       		
       			<td>
       				<s:property value="registerdate"/>
       			</td>
       			
       			<td>
       				<s:property value="age"/>
       			</td>
       			
       			<td>
       				<s:a href="updatePPerson.action?id=%{#person.id}">
       				update
    				</s:a>
       			</td>
       			
       			<td>
       				<s:a href="deletePerson.action?id=%{#person.id}" onchange="return del();">
       				delete
    				</s:a>
       			</td>
       			
       			</tr>
       		
       		</s:iterator>
       		
       	
       	</table>
      </body>
    </html>
    

    最后,还需要在web.xml中配置struts的过滤器:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
    	xmlns="http://java.sun.com/xml/ns/javaee" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      
      <filter>
      	<filter-name>struts2</filter-name>
      	<filter-class>
      		org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
      	</filter-class>
      </filter>
      
      <filter-mapping>
      	<filter-name>struts2</filter-name>
      	<url-pattern>/*</url-pattern>
      </filter-mapping>
      
      
    </web-app>
    

    访问http://localhost:8080/hibernate/register.jsp,增加用户和显示所有用户信息的功能就实现了。

    值得补充的一点是:session的beginTransaction()方法底层调用了java.sql.Connection类的setAutoCommit(false)方法,即所有的SQL语句执行完之后再提交,而setAutoCommit(true)的意思是一条SQL语句执行完毕立刻进行提交,相当于一条SQL语句就是一个事务。而我们在一个事务里面可能执行多个命令(SQL语句),因此通常情况下,使用JDBC的话,通常将setAutoCommit方法的参数设置为false,因为它的默认值为true。那么什么时候进行提交呢?其实session的commit方法底层就是通过调用Connection的commit方法实现提交。

    下面,进一步完善,点击用户名列出用户所有信息:

    在DBPerson.java中增加方法:

    public static Person getPersonById(Integer id)
    	{
    		
    		Session session = HibernateUtil.currentSession();
    		Transaction tx = session.beginTransaction(); // 开启事务
    		
    		Person person = null;
    		
    		try
    		{
    			// Object get(Class clazz, Serializable id)
    			// Integer实现了Serializable接口(没有任何方法,一个标识性的接口)表示可以序列化到硬盘上也可以从硬盘或者网络恢复到内存里
    			person = (Person)session.get(Person.class, id); 
    			
    			tx.commit();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("查看用户异常发生");
    			if(null != tx)
    				tx.rollback();
    		}
    		finally
    		{
    			HibernateUtil.closeSession(session);
    		}
    		
    		return person;
    	}
    

    struts.xml中配置action:

    <action name="getPerson" class="com.test.action.PersonAction" method="getPerson">
    				<result name="success">/getPerson.jsp</result>
    			</action>
    

    然后在PersonAction.java中写方法getPerson():

    	public String getPerson() throws Exception
    	{
    		Person person = DBPerson.getPersonById(id);
    		
    		HttpServletRequest request = ServletActionContext.getRequest();
    	
    		request.setAttribute("person", person);
    		
    		return SUCCESS;
    	}
    

    然后是展示页面:getPerson.jsp:

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%@ taglib uri="/struts-tags" prefix="s"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'getPerson.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    	<!--
    	<link rel="stylesheet" type="text/css" href="styles.css">
    	-->
    
      </head>
      
      <body>
        <table width="70%" align="center" border="1">
        	
        	<tr>
        		<td>username:
        		</td>
        		<td><s:property value="#request.person.username">
        		</td>
        	</tr>
        	
        	<tr>
        		<td>password:
        		</td>
        		<td><s:property value="#request.person.password">
        		</td>
        	</tr>
     	
     		<tr>
        		<td>age:
        		</td>
        		<td><s:property value="#request.person.age">
        		</td>
        	</tr>
        	
        	<tr>
        		<td>registerdate:
        		</td>
        		<td><s:property value="#request.person.registerdate">
        		</td>
        	</tr> 
        	
        	<tr>
        		<td colspan="2"> <input type="button" value="back" onclick="javascript:history.back();">
        		</td>
        	</tr> 
        	  
        </table>
      </body>
    </html>
    

    测试,其中一个实例如下:

    接下来实现删除操作:

    在DBPerson中增加removePerson方法:

    	public static void removePerson(Integer id)
    	{
    		Session session = HibernateUtil.currentSession();
    		Transaction tx = session.beginTransaction(); // 开启事务
    		
    		try
    		{
    			// 从数据库中查询出主键为id的记录所对应的对象,删除对象,hibernate就知道要删除的对应的记录是哪个了
    			Person person = (Person)session.get(Person.class, id);
    			session.delete(person);
    			
    			tx.commit();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("删除用户异常发生");
    			if(null != tx)
    			{
    				tx.rollback();
    			}
    		}
    		finally
    		{
    			HibernateUtil.closeSession(session);
    		}
    	}
    

    然后配置struts.xml:

    	<action name="deletePerson" class="com.test.action.PersonAction" method="deletePerson">
    				<result name="success">/listAll.jsp</result>
    			</action>
    

    然后是PersonAction.java中增加deletePerson方法:

    	public String deletePerson() throws Exception
    	{
    		DBPerson.removePerson(id);
    		
    		List<Person> list = DBPerson.listAll();
    		
    		HttpServletRequest request = ServletActionContext.getRequest();
    		
    		request.setAttribute("list", list);
    		
    		return SUCCESS;
    	}
    

    重启服务器,测试通过即可。

    最后,实现update操作:

    在struts.xml中配置信息:

    <action name="updatePPerson" class="com.test.action.PersonAction" method="getPerson">
    				<result name="success">/updatePerson.jsp</result>
    			</action>
    

    新建一个updatePerson.jsp:(引入struts标签库,页面编码UTF-8)

      <body>
       	<form action="updatePerson.action" method="post">
       		username:<s:textfield name="username" value="%{#request.person.username}" readonly="true"></s:textfield> <br />
    		password:<s:textfield name="password" value="%{#request.person.password}" ></s:textfield> <br />
    		age:<s:textfield name="age" value="%{#request.person.age}"></s:textfield> <br />
    		registerdate:<s:textfield name="registerdate" value="%{#request.person.registerdate}" readonly="true"></s:textfield>
       		<s:hidden name="id" value="%{#request.person.id}"></s:hidden>
       		<input type="submit" value="submit" />
       	</form> 
      </body>
    

    在DBPerson.java中增加updatePerson方法:

    	public static void updatePerson(Person person)
    	{
    		Session session = HibernateUtil.currentSession();
    		Transaction tx = session.beginTransaction(); // 开启事务
    		
    		try
    		{
    			session.update(person);
    			
    			tx.commit();
    		}
    		catch(Exception ex)
    		{
    			System.out.println("修改用户信息异常发生");
    			if(null != tx)
    				tx.rollback();
    		}
    		finally
    		{
    			HibernateUtil.closeSession(session);
    		}
    		
    	}
    

    在struts.xml中增加action:

    			
    			<action name="updatePerson" class="com.test.action.PersonAction" method="updatePerson">
    				<result name="success">/listAll.jsp</result>
    			</action>
    

    在PersonAction.java中增加updatePerson方法:

    	public String updatePerson() throws Exception
    	{
    		Person person = DBPerson.getPersonById(id);
    		
    		person.setPassword(password);
    		person.setAge(age);
    		
    		/*
    		也可以通过
    		Person person = new Person();
    		person.setId(id);
    		person.setUsername(username);
    		...
    		的方式,但是个类里忘记定义registerdate属性了,因此采用上面先查后该的方式
    		 */
    		
    		DBPerson.updatePerson(person);
    		
    		List<Person> list = DBPerson.listAll();
    		
    		HttpServletRequest request = ServletActionContext.getRequest();
    		
    		request.setAttribute("list", list);
    		
    		return SUCCESS;
    	}
    

    至此,增删查改各个功能全部实现。但是现在这个小应用有个很严重的问题:增加用户的时候列出所有用户的时候,刷新页面,重复的用户信息会不断的插入到数据库中同时显示到页面上。

    比如注册信息列出所有用户地址栏为:http://localhost:8080/hibernate/save.action?username=%E5%93%88%E5%93%88&password=123&age=12,那么重复刷新,save操作会被不断的执行,解决方法就是采用重定向的方式而不是使用请求转发。

    需要更改struts.xml中result的部分,增加一个属性:redirect

    <action name="save" class="com.test.action.PersonAction" method="save"> <!-- 这里关注点在hibernate,因此struts的输入校验就省略了 -->
    				<result name="success" type="redirect">/listAll.jsp</result>
    			</action>
    

    删除PersonAction中如下部分(因为是重定向的方式,是另一个request,当前request已经不起作用了):

    	
    		List<Person> list = DBPerson.listAll();
    		
    		HttpServletRequest request = ServletActionContext.getRequest();
    		
    		request.setAttribute("list", list);
    

    需要更改listAll.jsp如下的部分(导包和访问DBPerson的listAll方法):

    <%@ page language="java" import="java.util.*, com.hibernate.model.Person, com.hibernate.persistence.DBPerson" pageEncoding="utf-8"%>
    <%@ taglib uri="/struts-tags" prefix="s" %>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    <%		List<Person> list = DBPerson.listAll();
    				
    		request.setAttribute("list", list);
     %>
    

    这样增加用户后跳转页面链接变成了http://localhost:8080/hibernate/listAll.jsp,不会出现重复注册的现象了。 

  • 相关阅读:
    Oracle分页之一:最普通的分页方式
    MSSQL存储过程学习笔记一:关于存储过程
    MSSQL自动备份数据库
    小试JQuery的AutoComplete插件
    利用面向对象的方式来使用JS
    Oracle分页之三:利用PagerView来实现无刷新GridView
    由于 ASP.NET 进程标识对全局程序集缓存没有读权限,因此未能执行请求。错误: 0x80131902
    终端服务器超出最大允许连接数
    从苹果的Siri说起:云搜索与人工智能
    [转]为什么我们程序员难晋升
  • 原文地址:https://www.cnblogs.com/Code-Rush/p/4805004.html
Copyright © 2011-2022 走看看