星空网 > 软件开发 > Java

JPA J2SE 桌面应用范例

JPA虽然是Java EE规范的一部分,但是可以在J2SE环境下单独使用,不需要Web相关容器。本篇主要通过Netbeans 自动生成的代码来学习JPA相关内容。

1.打开Netbeans 8.1 添加Mysql数据库连接

JPA J2SE 桌面应用范例

 

2.新建Java应用程式,输入项目名称JpaExample

JPA J2SE 桌面应用范例

3.在项目中右键新建其他,选择主样例窗体/详细样例窗体,输入类名称和包名称

JPA J2SE 桌面应用范例

4.选择第一步建立的数据库连接->选择表(user),右侧为程式中用到的栏位,默认是全部带到右边,不需要的可以移到左边

JPA J2SE 桌面应用范例

5.选择在Swing界面上显示的栏位,默认全部在右边表示全选,不需要的可以移到左边。点击完成

JPA J2SE 桌面应用范例

6.查看生成的类,persistence.

JPA J2SE 桌面应用范例

7.运行,点击New就可以新增内容,Delete删除上面Jtable选中的数据,Save保存到数据库,Refresh 刷新数据,主要是数据库数据与JPA同步,如果数据库有变动,刷新后本地JPA显示变动后数据。

JPA J2SE 桌面应用范例

8.代码分析,完整MasterDetailForm.java如下。

JPA J2SE 桌面应用范例JPA J2SE 桌面应用范例
package com.cc.jpa;import java.awt.EventQueue;import java.beans.Beans;import java.util.ArrayList;import java.util.List;import javax.persistence.RollbackException;import javax.swing.JFrame;import javax.swing.JPanel;/** * * @author dev */public class MasterDetailForm extends JPanel {    public MasterDetailForm() {    initComponents();    if (!Beans.isDesignTime()) {      entityManager.getTransaction().begin();    }  }  /**   * This method is called from within the constructor to initialize the form.   * WARNING: Do NOT modify this code. The content of this method is always   * regenerated by the Form Editor.   */  @SuppressWarnings("unchecked")  // <editor-fold defaultstate="collapsed" desc="Generated Code">               private void initComponents() {    bindingGroup = new org.jdesktop.beansbinding.BindingGroup();    entityManager = java.beans.Beans.isDesignTime() ? null : javax.persistence.Persistence.createEntityManagerFactory("ccPU").createEntityManager();    query = java.beans.Beans.isDesignTime() ? null : entityManager.createQuery("SELECT u FROM User u");    list = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(query.getResultList());    masterScrollPane = new javax.swing.JScrollPane();    masterTable = new javax.swing.JTable();    idLabel = new javax.swing.JLabel();    usernmaeLabel = new javax.swing.JLabel();    passwordLabel = new javax.swing.JLabel();    nameLabel = new javax.swing.JLabel();    idField = new javax.swing.JTextField();    usernmaeField = new javax.swing.JTextField();    passwordField = new javax.swing.JTextField();    nameField = new javax.swing.JTextField();    saveButton = new javax.swing.JButton();    refreshButton = new javax.swing.JButton();    newButton = new javax.swing.JButton();    deleteButton = new javax.swing.JButton();    FormListener formListener = new FormListener();    org.jdesktop.swingbinding.JTableBinding jTableBinding = org.jdesktop.swingbinding.SwingBindings.createJTableBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, list, masterTable);    org.jdesktop.swingbinding.JTableBinding.ColumnBinding columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${id}"));    columnBinding.setColumnName("Id");    columnBinding.setColumnClass(Integer.class);    columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${usernmae}"));    columnBinding.setColumnName("Usernmae");    columnBinding.setColumnClass(String.class);    columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${password}"));    columnBinding.setColumnName("Password");    columnBinding.setColumnClass(String.class);    columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${name}"));    columnBinding.setColumnName("Name");    columnBinding.setColumnClass(String.class);    bindingGroup.addBinding(jTableBinding);    masterScrollPane.setViewportView(masterTable);    idLabel.setText("Id:");    usernmaeLabel.setText("Usernmae:");    passwordLabel.setText("Password:");    nameLabel.setText("Name:");    org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement.id}"), idField, org.jdesktop.beansbinding.BeanProperty.create("text"));    binding.setSourceUnreadableValue("null");    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement != null}"), idField, org.jdesktop.beansbinding.BeanProperty.create("enabled"));    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement.usernmae}"), usernmaeField, org.jdesktop.beansbinding.BeanProperty.create("text"));    binding.setSourceUnreadableValue("null");    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement != null}"), usernmaeField, org.jdesktop.beansbinding.BeanProperty.create("enabled"));    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement.password}"), passwordField, org.jdesktop.beansbinding.BeanProperty.create("text"));    binding.setSourceUnreadableValue("null");    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement != null}"), passwordField, org.jdesktop.beansbinding.BeanProperty.create("enabled"));    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement.name}"), nameField, org.jdesktop.beansbinding.BeanProperty.create("text"));    binding.setSourceUnreadableValue("null");    bindingGroup.addBinding(binding);    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement != null}"), nameField, org.jdesktop.beansbinding.BeanProperty.create("enabled"));    bindingGroup.addBinding(binding);    saveButton.setText("Save");    saveButton.addActionListener(formListener);    refreshButton.setText("Refresh");    refreshButton.addActionListener(formListener);    newButton.setText("New");    newButton.addActionListener(formListener);    deleteButton.setText("Delete");    binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, masterTable, org.jdesktop.beansbinding.ELProperty.create("${selectedElement != null}"), deleteButton, org.jdesktop.beansbinding.BeanProperty.create("enabled"));    bindingGroup.addBinding(binding);    deleteButton.addActionListener(formListener);    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);    this.setLayout(layout);    layout.setHorizontalGroup(      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()        .addComponent(newButton)        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addComponent(deleteButton)        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addComponent(refreshButton)        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addComponent(saveButton)        .addContainerGap())      .addGroup(layout.createSequentialGroup()        .addContainerGap()        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)          .addComponent(idLabel)          .addComponent(usernmaeLabel)          .addComponent(passwordLabel)          .addComponent(nameLabel))        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)          .addComponent(idField, javax.swing.GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)          .addComponent(usernmaeField, javax.swing.GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)          .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)          .addComponent(nameField, javax.swing.GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE))        .addContainerGap())      .addGroup(layout.createSequentialGroup()        .addContainerGap()        .addComponent(masterScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)        .addContainerGap())    );    layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteButton, newButton, refreshButton, saveButton});    layout.setVerticalGroup(      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)      .addGroup(layout.createSequentialGroup()        .addContainerGap()        .addComponent(masterScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 130, Short.MAX_VALUE)        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)          .addComponent(idLabel)          .addComponent(idField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)          .addComponent(usernmaeLabel)          .addComponent(usernmaeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)          .addComponent(passwordLabel)          .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)          .addComponent(nameLabel)          .addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)          .addComponent(saveButton)          .addComponent(refreshButton)          .addComponent(deleteButton)          .addComponent(newButton))        .addContainerGap())    );    bindingGroup.bind();  }  // Code for dispatching events from components to event handlers.  private class FormListener implements java.awt.event.ActionListener {    FormListener() {}    public void actionPerformed(java.awt.event.ActionEvent evt) {      if (evt.getSource() == saveButton) {        MasterDetailForm.this.saveButtonActionPerformed(evt);      }      else if (evt.getSource() == refreshButton) {        MasterDetailForm.this.refreshButtonActionPerformed(evt);      }      else if (evt.getSource() == newButton) {        MasterDetailForm.this.newButtonActionPerformed(evt);      }      else if (evt.getSource() == deleteButton) {        MasterDetailForm.this.deleteButtonActionPerformed(evt);      }    }  }// </editor-fold>                @SuppressWarnings("unchecked")  private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {                           entityManager.getTransaction().rollback();    entityManager.getTransaction().begin();    java.util.Collection data = query.getResultList();    for (Object entity : data) {      entityManager.refresh(entity);    }    list.clear();    list.addAll(data);  }                         private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {                           int[] selected = masterTable.getSelectedRows();    List<com.cc.jpa.User> toRemove = new ArrayList<com.cc.jpa.User>(selected.length);    for (int idx = 0; idx < selected.length; idx++) {      com.cc.jpa.User u = list.get(masterTable.convertRowIndexToModel(selected[idx]));      toRemove.add(u);      entityManager.remove(u);    }    list.removeAll(toRemove);  }                        private void newButtonActionPerformed(java.awt.event.ActionEvent evt) {                         com.cc.jpa.User u = new com.cc.jpa.User();    entityManager.persist(u);    list.add(u);    int row = list.size() - 1;    masterTable.setRowSelectionInterval(row, row);    masterTable.scrollRectToVisible(masterTable.getCellRect(row, 0, true));  }                         private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {                          try {      entityManager.getTransaction().commit();      entityManager.getTransaction().begin();    } catch (RollbackException rex) {      rex.printStackTrace();      entityManager.getTransaction().begin();      List<com.cc.jpa.User> merged = new ArrayList<com.cc.jpa.User>(list.size());      for (com.cc.jpa.User u : list) {        merged.add(entityManager.merge(u));      }      list.clear();      list.addAll(merged);    }  }                       // Variables declaration - do not modify             private javax.swing.JButton deleteButton;  private javax.persistence.EntityManager entityManager;  private javax.swing.JTextField idField;  private javax.swing.JLabel idLabel;  private java.util.List<com.cc.jpa.User> list;  private javax.swing.JScrollPane masterScrollPane;  private javax.swing.JTable masterTable;  private javax.swing.JTextField nameField;  private javax.swing.JLabel nameLabel;  private javax.swing.JButton newButton;  private javax.swing.JTextField passwordField;  private javax.swing.JLabel passwordLabel;  private javax.persistence.Query query;  private javax.swing.JButton refreshButton;  private javax.swing.JButton saveButton;  private javax.swing.JTextField usernmaeField;  private javax.swing.JLabel usernmaeLabel;  private org.jdesktop.beansbinding.BindingGroup bindingGroup;  // End of variables declaration            public static void main(String[] args) {    /* Set the Nimbus look and feel */    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html     */    try {      for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {        if ("Nimbus".equals(info.getName())) {          javax.swing.UIManager.setLookAndFeel(info.getClassName());          break;        }      }    } catch (ClassNotFoundException ex) {      java.util.logging.Logger.getLogger(MasterDetailForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);    } catch (InstantiationException ex) {      java.util.logging.Logger.getLogger(MasterDetailForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);    } catch (IllegalAccessException ex) {      java.util.logging.Logger.getLogger(MasterDetailForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);    } catch (javax.swing.UnsupportedLookAndFeelException ex) {      java.util.logging.Logger.getLogger(MasterDetailForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);    }    //</editor-fold>    /* Create and display the form */    EventQueue.invokeLater(new Runnable() {      public void run() {        JFrame frame = new JFrame();        frame.setContentPane(new MasterDetailForm());        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.pack();        frame.setVisible(true);      }    });  }  }

View Code

主要部分:

entityManager初始化,ccPU是在persistence.

查看User.java文件会发现,Netbeans 生成的文件里面默认生成了几个Query,也可以根据需要自行添加,集中管理。

entityManager = java.beans.Beans.isDesignTime() ? null : javax.persistence.Persistence.createEntityManagerFactory("ccPU").createEntityManager();

query = java.beans.Beans.isDesignTime() ? null : entityManager.createQuery("SELECT u FROM User u");list = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(query.getResultList());

User:

JPA J2SE 桌面应用范例JPA J2SE 桌面应用范例
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */package com.cc.jpa;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeSupport;import java.io.Serializable;import javax.persistence.Basic;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.NamedQueries;import javax.persistence.NamedQuery;import javax.persistence.Table;import javax.persistence.Transient;/** * * @author dev */@Entity@Table(name = "user", catalog = "cc", schema = "")@NamedQueries({  @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),  @NamedQuery(name = "User.findById", query = "SELECT u FROM User u WHERE u.id = :id"),  @NamedQuery(name = "User.findByUsernmae", query = "SELECT u FROM User u WHERE u.usernmae = :usernmae"),  @NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),  @NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name")})public class User implements Serializable {  @Transient  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);  private static final long serialVersionUID = 1L;  @Id  @Basic(optional = false)  @Column(name = "id")  private Integer id;  @Basic(optional = false)  @Column(name = "usernmae")  private String usernmae;  @Basic(optional = false)  @Column(name = "password")  private String password;  @Basic(optional = false)  @Column(name = "name")  private String name;  public User() {  }  public User(Integer id) {    this.id = id;  }  public User(Integer id, String usernmae, String password, String name) {    this.id = id;    this.usernmae = usernmae;    this.password = password;    this.name = name;  }  public Integer getId() {    return id;  }  public void setId(Integer id) {    Integer oldId = this.id;    this.id = id;    changeSupport.firePropertyChange("id", oldId, id);  }  public String getUsernmae() {    return usernmae;  }  public void setUsernmae(String usernmae) {    String oldUsernmae = this.usernmae;    this.usernmae = usernmae;    changeSupport.firePropertyChange("usernmae", oldUsernmae, usernmae);  }  public String getPassword() {    return password;  }  public void setPassword(String password) {    String oldPassword = this.password;    this.password = password;    changeSupport.firePropertyChange("password", oldPassword, password);  }  public String getName() {    return name;  }  public void setName(String name) {    String oldName = this.name;    this.name = name;    changeSupport.firePropertyChange("name", oldName, name);  }  @Override  public int hashCode() {    int hash = 0;    hash += (id != null ? id.hashCode() : 0);    return hash;  }  @Override  public boolean equals(Object object) {    // TODO: Warning - this method won't work in the case the id fields are not set    if (!(object instanceof User)) {      return false;    }    User other = (User) object;    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {      return false;    }    return true;  }  @Override  public String toString() {    return "com.cc.jpa.User[ id=" + id + " ]";  }  public void addPropertyChangeListener(PropertyChangeListener listener) {    changeSupport.addPropertyChangeListener(listener);  }  public void removePropertyChangeListener(PropertyChangeListener listener) {    changeSupport.removePropertyChangeListener(listener);  }  }

View Code

New,主要是新增一个新的User对象,没有写到数据库中,主要是前端操作

com.cc.jpa.User u = new com.cc.jpa.User();entityManager.persist(u);list.add(u);int row = list.size() - 1;masterTable.setRowSelectionInterval(row, row);masterTable.scrollRectToVisible(masterTable.getCellRect(row, 0, true));

Delete,执行entityManager.remove(u)并没有写到数据库是因为有开启Transaction ,在保存的时候才会commit

int[] selected = masterTable.getSelectedRows(); 
List<com.cc.jpa.User> toRemove = new ArrayList<com.cc.jpa.User>(selected.length); for (int idx = 0; idx < selected.length; idx++) { com.cc.jpa.User u = list.get(masterTable.convertRowIndexToModel(selected[idx])); toRemove.add(u); entityManager.remove(u); } list.removeAll(toRemove);

Save,主要就是Transaction 的提交以及异常处理

try {
entityManager.getTransaction().commit();
entityManager.getTransaction().begin();
} catch (RollbackException rex) {
rex.printStackTrace();
entityManager.getTransaction().begin();
List<com.cc.jpa.User> merged = new ArrayList<com.cc.jpa.User>(list.size());
for (com.cc.jpa.User u : list) {
merged.add(entityManager.merge(u));
}
list.clear();
list.addAll(merged);
}


Refresh,同步数据库数据

entityManager.getTransaction().rollback();entityManager.getTransaction().begin();java.util.Collection data = query.getResultList();for (Object entity : data) {  entityManager.refresh(entity);}list.clear();list.addAll(data);

 

此范例主要是J2SE 下JPA的使用,实际项目中,可能设计很多table,程式也会相应的分层,如果只是学习一下JPA的特性熟悉下方法,这种方式也是没问题的。使用eclipse 创建JPA时 EclipseLink 需要单独下载,EclipseLink 前身是Oracle TopLink ,以前商业框架 ADF的一部分,用JPA 选择EclipseLink ,Hibernate 或其他实现都没有关系,而且只用JPA的功能,不用实现提供的一些扩展的话,切换使用都是没有问题的,就像JSP页面用不同的容器,Tomcat、Jetty、GlassFish结果是没差别的。

 

 




原标题:JPA J2SE 桌面应用范例

关键词:

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

威海海外仓:https://www.goluckyvip.com/tag/95346.html
maven 国外仓库:https://www.goluckyvip.com/tag/95347.html
中国十大海外仓:https://www.goluckyvip.com/tag/95348.html
德国站海外仓:https://www.goluckyvip.com/tag/95349.html
简易版:https://www.goluckyvip.com/tag/9535.html
美国 海外仓库:https://www.goluckyvip.com/tag/95350.html
武陵山大裂谷周围景点 武陵山大裂谷周围景点图片:https://www.vstour.cn/a/411233.html
南美旅游报价(探索南美洲的旅行费用):https://www.vstour.cn/a/411234.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流