169it科技资讯


当前位置:  编程技术>综合
本页文章导读:
    ▪spring+hibernate+事务      数据库连接泄漏是件可怕的事情,可能直接导致系统停止响应,另外因事务管理错误而导致数据出现不一致也是件可怕的事情,在后台系统中,这两个问题经常会结伴出现,本文将通过案例详......
    ▪MyEclipse中文乱码,编码格式设置,文件编码格式      一、设置新建常见文件的默认编码格式,也就是文件保存的格式。 在不对MyEclipse进行设置的时候,默认保存文件的编码,一般跟简体中文操作系统(如windows2000,windowsXP)的编码一致......
    ▪struts+spring+hibernate用jquery实现数据分页异步加载,页面不刷新        功能实现:分页,点击“加载更多”,将下一页的数据加载出来,页面不刷新。 用户entity类: package com.test.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; i......

[1]spring+hibernate+事务
    来源: 互联网  发布时间: 2014-02-18

数据库连接泄漏是件可怕的事情,可能直接导致系统停止响应,另外因事务管理错误而导致数据出现不一致也是件可怕的事情,在后台系统中,这两个问题经常会结伴出现,本文将通过案例详解使用Spring+Hibernate时可能导致问题的几种情况,希望对大家有所帮助。

文章比较长,如果你是遇到了问题正急求解决方案的话,可以先只读这一条:检查你的dao是否有直接使用session,修改为使用hibernateTemplate。
如果你希望更多的了解session, hibernateTemplate, transaction,请继续。

以下案例基于Struts2.3.1+Spring3.1.1+hibernate3.5.4完成,案例场景为对系统参数进行管理,涉及如下四个类:
Action PropertyAction
Service PropertyService/PropertyServiceImpl
DAO PropertyDAO(继承HibernateDaoSupport)
Entity SysProperty
1. Action中直接调用dao,未使用getHibernateTemplate
在一个复杂的多层架构系统中,事务控制在service中完成是更合理的,不应交由view层来控制,本文假定你是这么做的。一般我们代码的处理顺序为action->service->dao,然而总会有人会打破这种逻辑,打破可以,但要注意如下问题。
假定Action中有如下代码:
1 @Autowired
2 private PropertyDAO propertyDAO;
3 @Override
4 public String execute() throws Exception {
5      model = propertyDAO.get(1L);
6      return null;
7 }

这里action直接访问了dao中的get方法,因事务配置在service层,因此这里是没有事务控制的。
接下来我们看dao中的get方法,假定你的定义如下:
1 public SysProperty get(Long id) {
2     return (SysProperty) getSession().get(SysProperty.class, id);
3 }

代码很简单,getSession是父类HibernateDaoSupport中提供的方法,这里直接通过实体Id get得到结果。
接下来发布一下系统,访问页面,好像一切OK,但是,再刷新几下看看,可能你的连接池配置的比较大,要刷新多次,最后你发会现你的系统停止了响应。更直接点,调用你采用的连接池中的相关API检测下当前连接占用情况,你会发现刷新一次,被占用连接增加一个,可怕的事情发生了。
小结:在无事务管理器控制的情况下,通过getSession()打开的session不会被关闭,这个session所占用的数据库连接也不会被释放。

接下来,来点更可怕的,假设你的action中是这样的:
1 private List<Long> ids;
2 @Override
3 public String execute() throws Exception {
4   for(Long id: ids) {
5     results.add(propertyDAO.get(id));
6   }
7   return null;
8 }
9
这时尝试一次传入多个id进行请求,请求结束后检查下连接数,你发现传入多少ID,就有多少连接被占用,也就是说每一次对dao的get调用都会占用一个不可释放的连接。
小结:如果没有配置事务管理器且直接使用getSession得到session,每次getSession都会创建一个新的session,每个session占用一个新的数据库连接,session无线程级别的共享。

2. Action中直接调用dao,使用getHibernateTemplate
当然,你可能没遇到过前面的情况,因为你会在DAO中这样写代码:
1 public SysProperty get(Long id) {
2     return getHibernateTemplate().get(getEntityClass(), id);
3 }
那么是否就一切OK呢?如果只是想解决连接泄漏这个问题的话,答案是Yes,尝试多次请求,检查下你的连接池状况,没有连接泄漏的情况出现。所以简单而可靠的办法就是不再直接使用getSession,而是使用getHibernateTemplate进行相应操作,当需要使用session时,可以用下面的方法:
getHibernateTemplate().execute(new HibernateCallback<T>() {
  public T doInHibernate(Session session) throws HibernateException {
    Query queryObject = session.createQuery(hql);
    //....
  }
});
But...如果你的系统真的出现了连接泄漏,可能你需要关注更多。
还是前面Action中根据ID循环查询的操作,在这个案例中,每一次Dao的get方法都将重复这样的逻辑:创建session,分配session一个连接,关闭session,释放session占用的连接,也就是说session不会在线程级别共享。让我们继续,进入第三种情况来进一步说明。

3. Service未合理配置事务管理器
绝大部分情况下我们会给service配置事务管理器,可能是通过xml或是@Transactional注解,但并非配置了就OK了。看下面的例子:
@Transactional
public class PropertyServiceImpl implements PropertyService {
    @Autowired
    private PropertyDAO propertyDAO;
  
    @PostConstruct
    public void init(){
        propertyDAO.get(1L);
    }
    //
}
当然,假定你的DAO还是写成了这样:
public SysProperty get(Long id) {
    return (SysProperty) getSession().get(SysProperty.class, id);
}
你期望在service初始化好后做一些数据库操作,你也给service配置了事务管理器,接下来你启动应用,然后检查连接池中的连接数,你会发现有连接未被释放!可能你会果断的修改dao中的方法:
public SysProperty get(Long id) {
    return getHibernateTemplate().get(getEntityClass(), id);
}
然后你会理所当然的认为,即使配置了事务管理器,依然不能使用getSession(),事实可能并非如此。
我们调整一下代码,不在init中调用dao中的get方法,改为如下:
public SysProperty getProperty(Long id) {
    return propertyDAO.get(id);
}
然后DAO继续使用(SysProperty) getSession().get(SysProperty.class, id),而action中的调用修改为:
@Autowired
private PropertyService propertyService;
@Override
public String execute() throws Exception {
    model = propertyService.getProperty(1L);
    return null;
}
重新发布,调用,检查连接数,发现无被占用连接存在。
小结:如果正确配置了事务管理器,getSession是安全的。

这时要清楚spring是通过AOP来实现事务控制的,而@PostConstruct方法不会受AOP控制,因此上面的init方法等于无事务管理器。
那么再回头来说,是否只要dao中使用getHibernateTemplate就不会有问题呢?
假定service中的@PostConstruct方法如下:
@PostConstruct
public void init(){
    SysProperty p = new SysProperty();
    p.setName("test_property_1");
    propertyDAO.save(p);
}
强调一下,前面已经提到这个方法不受spring的事务管理器控制。
假定DAO中的save方法如下:
public void save(SysProperty o){
    getSession().save(o);
}
启动一下应用,检查一下连接数,再检查一下数据是否有存储到数据库中。因为我们直接使用了getSession,因此连接不会释放,这点前面已经提到,但同时我们还将发现数据没有被存储,当然这个也好理解,因为上面已经提到这个方法未配置事务管理器。
小结:通过getSession().save()保存数据时,事务不会自动提交。

现在再修改下DAO中的save方法:
public void save(SysProperty o){
    getHibernateTemplate().save(o);
}
启动一下应用,检查一下连接数,再检查一下数据是否有存储到数据库中。因为我们使用hibernateTemplate,因此连接有释放,这点前面已经提到,但同时我们还发现数据也已存储到数据库中,说明hibernateTemplate会自动提交事务。
小结:如果未配置事务管理器,通过hibernateTemplate操作时,会自动创建并提交事务。

所以如果你觉得使用hibernateTemplate就OK了,那就要小心下面的代码了:
@PostConstruct
public void init(){
  //1. 从你的账号A中扣除一万块
  //2. 这里的代码抛出了异常
  //3. 将你的账号B中增加一万块
}
如果上面的第2步出现了异常,那么因为1的事务已经提交,而3却没有执行,最终导致了数据的不一致,后果和连接泄漏一样严重!
除了@PostConstruct,还有其它原因会导致@Transactional无效,假定我们的service配置了事务管理器,但存在如下代码:
pubic void someServiceMethod(){
  new Thread(){
    public void run(){
        doSomethingLater();
    }
  }
}

public void doSomethingLater(){
  //做一系列数据库操作
}
那么你可以去验证下doSomethingLater是否受事务管理器控制,事实上并不会,所以你需要理解spring AOP的机制,否则一个小坑会酿成灾难。
这里还有一种情况,你不是在类上面配置@Transactional,而是在方法上面配置,假定存在如下的代码:
//该方法不需要事务控制
public void method1(){
    method2();
}

//下面的方法需要事务控制
@Transactional
public void method2(){
 &nbs

    
[2]MyEclipse中文乱码,编码格式设置,文件编码格式
    来源: 互联网  发布时间: 2014-02-18
一、设置新建常见文件的默认编码格式,也就是文件保存的格式。
在不对MyEclipse进行设置的时候,默认保存文件的编码,一般跟简体中文操作系统(如windows2000,windowsXP)的编码一致,即GBK。
在简体中文系统下,ANSI 编码代表 GBK编码;在日文操作系统下,ANSI 编码代表 JIS 编码。
Window-->Preferences-->General -->content Types
在右侧窗口中展开每一个子项,依次输入编码格式,如“UTF-8”点击“update”就设置好了。
例如:在以上设置中,设置jsp默认编码格式为“UTF-8”,那么以后新建的jsp文件,都是以“UTF-8”的格式保存的。
同样设置java默认编码格式为“UTF-8”,那么以后新建的java文件,都是以“UTF-8”的格式保存的。
设置html默认编码格式为“UTF-8”,那么以后新建的html文件,都是以“UTF-8”的格式保存的。
以上设置了文件的保存编码格式,默认的打开方式(解码方式)会跟保存编码格式一样。   

二、设置新建其他文件的默认编码格式,即文件保存格式。
    在第一项设置中,只是设置了常用文件类型的编码格式。如果新建一个文件,没有后缀名,也就是不指名文件类型,那么MyEclipse无法判断该用何种编码

格式保存,就用默认文件保存编码格式GBK进行保存。这个默认保存编码格式,可用如下方法修改:
window-->preferences-->general-->workspace 在右侧 Text file encoding ->Other    选择UTF-8

三、单个文件查看编码格式(也就是打开文件用的编码格式)更改。这个方法没有更改文件的编码格式,只是告诉MyEclipse用何种编码格式进行解码。
在要查看的文件上,如文件Test.java上右键-->Properties-->Resource在右侧Text file encoding ->Other,可以进行修改。
比如说,你在“一”里面设置*.java文件的编码格式为“UTF-8”,那么你打开后缀名为java的文件,默认就会以“UTF-8”格式进行解码。如果有人给了你一个

编写好的java文件,但是他是用GBK编码保存的,那么你现在打开的时候,会显示乱码。在这个文件上右键-->Properties-->Resource在右侧Text file

encoding ->Other ->GBK,就可以正常显示里面的内容了。

四、指定MyEclipse编写文件时页面内容数据编码格式,如html,或者jsp网页中提交表单前,用户输入的信息编码格式,配置文件xml中的参数编码格式等。
主要是通知浏览器或服务器,传输的数据的编码格式。
window-->Preferences-->MyEclipse-->Files and Editors-->选择子项-->Encoding
例如:选择UTF-8,
新建jsp文件的时候,contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"就会自动生成,不用每次去手工更改了。
新建html文件的时候,http-equiv="Content-Type" content="text/html; charset=UTF-8"
新建xml文件的时候,<?xml version="1.0" encoding="UTF-8"?>

以上是自己总结的,如果错误,请指出。问题如下
1、如果别人给了给的一个project,编码格式为GBK,我自己使用的编码格式为UTF-8,导入这个project后,显示乱码,需要进行哪些修改?
我想知道最有用的,必须进行的修改方法。如果我上面的几点依次全部更改,是可以正常显示的。但是更改以后,查看自己的代码又会出现中文乱码了。
其实这个问题就是,如何让两个Project使用不同编码(如UTF-8和GBK),而都可以正常查看,不出现乱码。
作者:xuxu198899223 发表于2013-1-14 10:08:36 原文链接
阅读:7 评论:0 查看评论

    
[3]struts+spring+hibernate用jquery实现数据分页异步加载,页面不刷新
    来源: 互联网  发布时间: 2014-02-18

 

功能实现:分页,点击“加载更多”,将下一页的数据加载出来,页面不刷新。

用户entity类:

package com.test.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="t_user",catalog="scott")
public class User implements Serializable{

private int user_id;

private String user_name;

private String user_sex;

private String user_class;

public User(){

}

public User(String user_name,String user_sex,String user_class){
this.user_name = user_name;
this.user_sex=user_sex;
this.user_class=user_class;
}

@Id
@Column(name="user_id")
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="user_seq")
@SequenceGenerator(sequenceName="user_seq",name="user_seq")
public int getUser_id() {
return user_id;
}

public void setUser_id(int user_id) {
this.user_id = user_id;
}

@Column(name="user_name",length=24)
public String getUser_name() {
return user_name;
}

public void setUser_name(String user_name) {
this.user_name = user_name;
}

@Column(name="user_sex",length=4)
public String getUser_sex() {
return user_sex;
}

public void setUser_sex(String user_sex) {
this.user_sex = user_sex;
}

@Column(name="user_class",length=24)
public String getUser_class() {
return user_class;
}

public void setUser_class(String user_class) {
this.user_class = user_class;
}




}


接口类:

package com.test.user.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.test.model.User;

@Service
public interface UserService {

/*******
* 分页加载
* @param pagesize
* @param pagenow
* @return
* @throws Exception
*/
public List<User> getAllUserByPage(int pagesize,int pagenow) throws Exception;

/*********
* 获取用户总记录数
* @return
*/
public int getAllUserCount();

/*************
* 创建用户
*
* @param user
* @throws Exception
*/
public void createUser(User user) throws Exception;

}


实现上面接口

package com.test.user.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.test.model.User;
import com.test.user.dao.UserDao;
import com.test.user.service.UserService;

@Component
public class UserServiceImpl implements UserService {

@Resource
private UserDao userDao;

/*******
* 分页加载
* @param pagesize
* @param pagenow
* @return
* @throws Exception
*/
public List<User> getAllUserByPage(int pagesize,int pagenow) throws Exception{
return this.userDao.getAllUserByPage(pagesize, pagenow);
}

/*********
* 获取用户总记录数
* @return
*/
public int getAllUserCount(){
return this.userDao.getAllUserCount();
}

@Override
public void createUser(User user) throws Exception {
this.userDao.createUser(user);

}

}


Dao底层操作类:

package com.test.user.dao;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.test.model.User;

@Repository
public class UserDao extends HibernateDaoSupport {

private JdbcTemplate jdbcTemplate;

@Resource
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

@Resource
public void setSessionFactoryOverride(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}


/*******
* 分页加载
* @param pagesize
* @param pagenow
* @return
* @throws Exception
*/
public List<User> getAllUserByPage(int pagesize,int pagenow) throws Exception{
Query q=this.getSession().createQuery("from User");
q.setFirstResult((pagenow-1)*pagesize);
q.setMaxResults(pagesize);
List li=q.list();
return li;
}

/*********
* 获取用户总记录数
* @return
*/
public int getAllUserCount(){
int count=0;
Query q=this.getSession().createQuery("select count(*) from User");
count=Integer.parseInt(q.uniqueResult().toString());
return count;
}

public void createUser(User user) throws Exception {
this.getHibernateTemplate().save(user);

}


public List<User> getAllUser() throws Exception {

// return this.getHibernateTemplate().find("from User");

// 用jdbcTemplate查询
List<User> list = new ArrayList<User>();
SqlRowSet rs = this.jdbcTemplate
.queryForRowSet("select user_id,user_name,user_sex,user_class from t_user");
while (rs.next()) {
User user = new User();
user.setUser_id(rs.getInt("user_id"));
user.setUser_name(rs.getString("user_name"));
user.setUser_sex(rs.getString("user_sex"));
user.setUser_class(rs.getString("user_class"));
list.add(user);
}
return list;
}

}


控制器Action类:

package com.test.user.action;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONArray;

import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Controller;

import com.opensymphony.xwork2.ActionSupport;
import com.test.model.User;
import com.test.user.service.UserService;

@Controller
public class UserAction extends ActionSupport {

@Resource
private UserService userService;

private List<User> userList = new ArrayList<User>();

public List<User> getUserList() {
return userList;
}

public void setUserList(List<User> userList) {
this.userList = userList;
}

/************************分页**************************/
private int pagesize;//每页显示多少条
private int currentPage;//当前页

public int getPagesize() {
return pagesize;
}

public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}

public int getCurrentPage() {
return currentPage;
}

public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}

/*************
* 初始化加载用户数据
* @return
*/
public String getAllUserInit(){
try {
this.pagesize=7;//先加载 7 条数据
this.currentPage=1;//初始时设置当前页为1
this.userList=this.userService.getAllUserByPage(this.pagesize, this.currentPage);
} catch (Exception e) {
e.printStackTrace();
}
return "all";
}

/***********
* 分页-异步加载
* @return
*/
public String getAllUserByPage(){
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
//response.setHeader("Cache-Control", "no-cache");
try {
this.pagesize=7;//每次加载7条数据
System.out.println("当前页"+this.currentPage);
this.userList=this.userService.getAllUserByPage(this.pagesize, this.currentPage);
System.out.println("加载数据:"+this.userList.size());
PrintWriter out = response.getWriter();
JSONArray json=JSONArray.fromObject(this.userList);
out.print(json);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


}


spring配置文件applicationContext-datasource.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass"><value>oracle.jdbc.driver.OracleDriver</value></property>
<property name="jdbcUrl"><value>jdbc:oracle:t

    
最新技术文章:
▪error while loading shared libraries的解決方法    ▪版本控制的极佳实践    ▪安装多个jdk,多个tomcat版本的冲突问题
▪简单选择排序算法    ▪国外 Android资源大集合 和个人学习android收藏    ▪.NET MVC 给loading数据加 ajax 等待loading效果
▪http代理工作原理(3)    ▪关注细节-TWaver Android    ▪Spring怎样把Bean实例暴露出来?
▪java写入excel2007的操作    ▪http代理工作原理(1)    ▪浅谈三层架构
▪http代理工作原理(2)    ▪解析三层架构……如何分层?    ▪linux PS命令
▪secureMRT Linux命令汉字出现乱码    ▪把C++类成员方法直接作为线程回调函数    ▪weak-and算法原理演示(wand)
▪53个要点提高PHP编程效率    ▪linux僵尸进程    ▪java 序列化到mysql数据库中
▪利用ndk编译ffmpeg    ▪活用CSS巧妙解决超长文本内容显示问题    ▪通过DBMS_RANDOM得到随机
▪CodeSmith 使用教程(8): CodeTemplate对象    ▪android4.0 进程回收机制    ▪仿天猫首页-产品分类
▪从Samples中入门IOS开发(四)------ 基于socket的    ▪工作趣事 之 重装服务器后的网站不能正常访    ▪java序列化学习笔记
▪Office 2010下VBA Addressof的应用    ▪一起来学ASP.NET Ajax(二)之初识ASP.NET Ajax    ▪更改CentOS yum 源为163的源
▪ORACLE 常用表达式    ▪记录一下,AS3反射功能的实现方法    ▪u盘文件系统问题
▪java设计模式-观察者模式初探    ▪MANIFEST.MF格式总结    ▪Android 4.2 Wifi Display核心分析 (一)
▪Perl 正则表达式 记忆方法    ▪.NET MVC 给loading数据加 ajax 等待laoding效果    ▪java 类之访问权限
▪extjs在myeclipse提示    ▪xml不提示问题    ▪Android应用程序运行的性能设计
▪sharepoint 2010 自定义列表启用版本记录控制 如    ▪解决UIScrollView截获touch事件的一个极其简单有    ▪Chain of Responsibility -- 责任链模式
▪运行skyeye缺少libbfd-2.18.50.0.2.20071001.so问题    ▪sharepoint 2010 使用sharepoint脚本STSNavigate方法实    ▪让javascript显原型!
▪kohana基本安装配置    ▪MVVM开发模式实例解析    ▪sharepoint 2010 设置pdf文件在浏览器中访问
▪spring+hibernate+事务    ▪MyEclipse中文乱码,编码格式设置,文件编码格    ▪struts+spring+hibernate用jquery实现数据分页异步加
▪windows平台c++开发"麻烦"总结    ▪Android Wifi几点    ▪Myeclipse中JDBC连接池的配置
▪优化后的冒泡排序算法    ▪elasticsearch RESTful搜索引擎-(java jest 使用[入门])    ▪MyEclipse下安装SVN插件SubEclipse的方法
▪100个windows平台C++开发错误之七编程    ▪串口转以太网模块WIZ140SR/WIZ145SR 数据手册(版    ▪初识XML(三)Schema
▪Deep Copy VS Shallow Copy    ▪iphone游戏开发之cocos2d (七) 自定义精灵类,实    ▪100个windows平台C++开发错误之八编程
▪C++程序的内存布局    ▪将不确定变为确定系列~Linq的批量操作靠的住    ▪DIV始终保持在浏览器中央,兼容各浏览器版本
▪Activity生命周期管理之三——Stopping或者Restarti    ▪《C语言参悟之旅》-读书笔记(八)    ▪C++函数参数小结
▪android Content Provider详解九    ▪简单的图片无缝滚动效果    ▪required artifact is missing.
▪c++编程风格----读书笔记(1)    ▪codeforces round 160    ▪【Visual C++】游戏开发笔记四十 浅墨DirectX教程
▪【D3D11游戏编程】学习笔记十八:模板缓冲区    ▪codeforces 70D 动态凸包    ▪c++编程风格----读书笔记(2)
▪Android窗口管理服务WindowManagerService计算Activity    ▪keytool 错误: java.io.FileNotFoundException: MyAndroidKey.    ▪《HTTP权威指南》读书笔记---缓存
▪markdown    ▪[设计模式]总结    ▪网站用户行为分析在用户市场领域的应用
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2017,169IT.COM,E-mail:www_169it_com#163.com(请将#改为@)

浙ICP备11055608号