----------厚厚发表于 2006年06月27日
网络上很多关于JAVA对Oracle中BLOB、CLOB类型字段的操作说明,有的不够全面,有的不够准确,甚至有的简直就是胡说八道。最近的项目正巧用到了这方面的知识,在这里做个总结。 环境: Database: Oracle 9i App Server: BEA Weblogic 8.14 表结构: CREATE TABLE TESTBLOB (ID Int, NAME Varchar2(20), BLOBATTR Blob) CREATE TABLE TESTBLOB (ID Int, NAME Varchar2(20), CLOBATTR Clob) JAVA可以通过JDBC,也可以通过JNDI访问并操作数据库,这两种方式的具体操作存在着一些差异,由于通过App Server的数据库连接池JNDI获得的数据库连接提供的java.sql.Blob和java.sql.Clob实现类与JDBC方式提供的不同,因此在入库操作的时候需要分别对待;出库操作没有这种差异,因此不用单独对待。
一、BLOB操作 1、入库 (1)JDBC方式 //通过JDBC获得数据库连接 Class.forName("oracle.jdbc.driver.OracleDriver"); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:testdb", "test", "test"); con.setAutoCommit(false); Statement st = con.createStatement(); //插入一个空对象empty_blob() st.executeUpdate("insert into TESTBLOB (ID, NAME, BLOBATTR) values (1, "thename", empty_blob())"); //锁定数据行进行更新,注意“for update”语句 ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1 for update"); if (rs.next()) { //得到java.sql.Blob对象后强制转换为oracle.sql.BLOB oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBATTR"); OutputStream outStream = blob.getBinaryOutputStream(); //data是传入的byte数组,定义:byte[] data outStream.write(data, 0, data.length); } outStream.flush(); outStream.close(); con.commit(); con.close(); (2)JNDI方式 //通过JNDI获得数据库连接 Context context = new InitialContext(); ds = (DataSource) context.lookup("ORA_JNDI"); Connection con = ds.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); //插入一个空对象empty_blob() st.executeUpdate("insert into TESTBLOB (ID, NAME, BLOBATTR) values (1, "thename", empty_blob())"); //锁定数据行进行更新,注意“for update”语句 ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1 for update"); if (rs.next()) { //得到java.sql.Blob对象后强制转换为weblogic.jdbc.vendor.oracle.OracleThinBlob(不同的App Server对应的可能会不同) weblogic.jdbc.vendor.oracle.OracleThinBlob blob = (weblogic.jdbc.vendor.oracle.OracleThinBlob) rs.getBlob("BLOBATTR"); OutputStream outStream = blob.getBinaryOutputStream(); //data是传入的byte数组,定义:byte[] data outStream.write(data, 0, data.length); } outStream.flush(); outStream.close(); con.commit(); con.close(); 2、出库 //获得数据库连接 Connection con = ConnectionFactory.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); //不需要“for update” ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1"); if (rs.next()) { java.sql.Blob blob = rs.getBlob("BLOBATTR"); InputStream inStream = blob.getBinaryStream(); //data是读出并需要返回的数据,类型是byte[] data = new byte[input.available()]; inStream.read(data); inStream.close(); } inStream.close(); con.commit(); con.close(); 二、CLOB操作 1、入库 (1)JDBC方式 //通过JDBC获得数据库连接 Class.forName("oracle.jdbc.driver.OracleDriver"); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:testdb", "test", "test"); con.setAutoCommit(false); Statement st = con.createStatement(); //插入一个空对象empty_clob() st.executeUpdate("insert into TESTCLOB (ID, NAME, CLOBATTR) values (1, "thename", empty_clob())"); //锁定数据行进行更新,注意“for update”语句 ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1 for update"); if (rs.next()) { //得到java.sql.Clob对象后强制转换为oracle.sql.CLOB oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBATTR"); Writer outStream = clob.getCharacterOutputStream(); //data是传入的字符串,定义:String data char[] c = data.toCharArray(); outStream.write(c, 0, c.length); } outStream.flush(); outStream.close(); con.commit(); con.close(); (2)JNDI方式 //通过JNDI获得数据库连接 Context context = new InitialContext(); ds = (DataSource) context.lookup("ORA_JNDI"); Connection con = ds.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); //插入一个空对象empty_clob() st.executeUpdate("insert into TESTCLOB (ID, NAME, CLOBATTR) values (1, "thename", empty_clob())"); //锁定数据行进行更新,注意“for update”语句 ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1 for update"); if (rs.next()) { //得到java.sql.Clob对象后强制转换为weblogic.jdbc.vendor.oracle.OracleThinClob(不同的App Server对应的可能会不同) weblogic.jdbc.vendor.oracle.OracleThinClob clob = (weblogic.jdbc.vendor.oracle.OracleThinClob) rs.getClob("CLOBATTR"); Writer outStream = clob.getCharacterOutputStream(); //data是传入的字符串,定义:String data char[] c = data.toCharArray(); outStream.write(c, 0, c.length); } outStream.flush(); outStream.close(); con.commit(); con.close(); 2、出库 //获得数据库连接 Connection con = ConnectionFactory.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); //不需要“for update” ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1"); if (rs.next()) { java.sql.Clob clob = rs.getClob("CLOBATTR"); Reader inStream = clob.getCharacterStream(); char[] c = new char[(int) clob.length()]; inStream.read(c); //data是读出并需要返回的数据,类型是String data = new String(c); inStream.close(); } inStream.close(); con.commit(); con.close(); 需要注意的地方: 1、java.sql.Blob、oracle.sql.BLOB、weblogic.jdbc.vendor.oracle.OracleThinBlob几种类型的区别 2、java.sql.Clob、oracle.sql.CLOB、weblogic.jdbc.vendor.oracle.OracleThinClob几种类型的区别
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=839235
|
posted @
2007-07-24 16:43 华梦行 阅读(663) |
评论 (0) |
编辑 收藏
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<html>
<script language="javascript">
//test function with post method
function RequestByPost(value)
{
var data;
data = '<?xml version="1.0" encoding="utf-8"?>';
data = data + '<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">';
data = data + '<soapenv:Body>';
data = data + '<ns1:getUser xmlns:ns1="http://ss/">';
data = data + '<Name>'+"ffff"+'</Name>';
data = data + '</ns1:getUser>';
data = data + '</soapenv:Body>';
data = data + '</soapenv:Envelope>';
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
var URL="http://localhost:8080/myPj/SayHelloTo?wsdl";
xmlhttp.Open("POST",URL, false);
xmlhttp.SetRequestHeader ("Content-Type","text/xml; charset=UTF-8");
xmlhttp.SetRequestHeader ("SOAPAction","http://ss/SayHelloTo");
alert(data);
xmlhttp.Send(data);
alert( xmlhttp.responseText);
document.getElementById('mm').value=xmlhttp.responseText;
}
</script>
<title>
Call webservice with javascript and xmlhttp.
</title>
<body>
<div id="mm">
<input type="button" value="CallWebserviceByGet" onClick="RequestByGet(null)">
<input type="button" value="CallWebserviceByPost" onClick="RequestByPost('Zach')">
<input id="mm" type="text"/>
</body>
</html>
posted @
2007-07-17 16:18 华梦行 阅读(156) |
评论 (0) |
编辑 收藏
res://msxml.dll/defaultss.xsl
posted @
2007-07-13 08:24 华梦行 阅读(100) |
评论 (0) |
编辑 收藏
Result:
hello null
<?xml version="1.0" ?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://ss/"><soapenv:Body><ns1:sayHelloResponse><return>hello null</return></ns1:sayHelloResponse></soapenv:Body></soapenv:Envelope>
posted @
2007-07-06 13:10 华梦行 阅读(98) |
评论 (0) |
编辑 收藏
nodeType 属性
KingCMS官方网站
Microsoft
2006-1-31 21:18:32
作 用
辨识节点的DOM 型态。
基本语法
numNodeType = xmlDocNode.nodeType ;
说 明
此属性只读且传回一个数值。
有效的数值符合以下的型别:
1-ELEMENT
2-ATTRIBUTE
3-TEXT
4-CDATA
5-ENTITY REFERENCE
6-ENTITY
7-PI (processing instruction)
8-COMMENT
9-DOCUMENT
10-DOCUMENT TYPE
11-DOCUMENT FRAGMENT
12-NOTATION
范 例
numNodeType = xmlDoc.documentElement.nodeType;
alert(numNodeType);
posted @
2007-07-06 12:58 华梦行 阅读(124) |
评论 (0) |
编辑 收藏
type:表示所有的数据类型
Message: 指明被调用的函数的参数
Port: 指明服务的具体内容,包括输入输出
Binding: 服务所绑定的协议
addTwo Method invocation
Method parameter(s)
Method returned
int : "
731"
SOAP Request
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header/>
<S:Body>
<ns2:addTwo xmlns:ns2="http://my/">
<mm>55</mm>
<gg>676</gg>
</ns2:addTwo>
</S:Body>
</S:Envelope>
SOAP Response
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:addTwoResponse xmlns:ns2="http://my/">
<return>731</return>
</ns2:addTwoResponse>
</S:Body>
</S:Envelope>
<?xml version="1.0" encoding="UTF-8"?><!-- Published by JAX-WS RI at
http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2-hudson-112-M1. --><!-- Generated by JAX-WS RI at
http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2-hudson-112-M1. --><definitions xmlns:wsu="
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="
http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="
http://my/" xmlns:xsd="
http://www.w3.org/2001/XMLSchema" xmlns="
http://schemas.xmlsoap.org/wsdl/" targetNamespace="
http://my/" name="firstService">
<types>
<xsd:schema>
<xsd:import namespace="
http://my/" schemaLocation="
http://localhost:8080/ssServ/firstService?xsd=1"></xsd:import>
</xsd:schema>
</types>
<message name="usrLogin">
<part name="parameters" element="tns:usrLogin"></part>
</message>
<message name="usrLoginResponse">
<part name="parameters" element="tns:usrLoginResponse"></part>
</message>
<message name="addTwo">
<part name="parameters" element="tns:addTwo"></part>
</message>
<message name="addTwoResponse">
<part name="parameters" element="tns:addTwoResponse"></part>
</message>
<message name="userLogin">
<part name="parameters" element="tns:userLogin"></part>
</message>
<message name="userLoginResponse">
<part name="parameters" element="tns:userLoginResponse"></part>
</message>
<portType name="first">
<operation name="usrLogin">
<input message="tns:usrLogin"></input>
<output message="tns:usrLoginResponse"></output>
</operation>
<operation name="addTwo">
<input message="tns:addTwo"></input>
<output message="tns:addTwoResponse"></output>
</operation>
<operation name="userLogin">
<input message="tns:userLogin"></input>
<output message="tns:userLoginResponse"></output>
</operation>
</portType>
<binding name="firstPortBinding" type="tns:first">
<soap:binding transport="
http://schemas.xmlsoap.org/soap/http" style="document"></soap:binding>
<operation name="usrLogin">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal"></soap:body>
</input>
<output>
<soap:body use="literal"></soap:body>
</output>
</operation>
<operation name="addTwo">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal"></soap:body>
</input>
<output>
<soap:body use="literal"></soap:body>
</output>
</operation>
<operation name="userLogin">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal"></soap:body>
</input>
<output>
<soap:body use="literal"></soap:body>
</output>
</operation>
</binding>
<service name="firstService">
<port name="firstPort" binding="tns:firstPortBinding">
<soap:address location="
http://localhost:8080/ssServ/firstService"></soap:address>
</port>
</service>
</definitions>
从本质上说:客户端发一个请求即SOAP 请求头,到指定的web service地址wsdl,然后经过webservice的处理之后,返回一个 SOAP 相应, 然后再在客户端解析他们
posted @
2007-07-05 18:27 华梦行 阅读(165) |
评论 (0) |
编辑 收藏
程序员每天该做的事
1、总结自己一天任务的完成情况
最好的方式是写工作日志,把自己今天完成了什么事情,遇见了什么问题都记录下来,日后翻看好处多多
2、考虑自己明天应该做的主要工作
把明天要做的事情列出来,并按照优先级排列,第二天应该把自己效率最高的时间分配给最重要的工作
3、考虑自己一天工作中失误的地方,并想出避免下一次再犯的方法
出错不要紧,最重要的是不要重复犯相同的错误,那是愚蠢
4、考虑自己一天工作完成的质量和效率能否还能提高
一天只提高1%,365天你的效率就能提高多少倍你知道吗? (1+0.01)^365 = 37 倍
posted @
2007-07-05 11:33 华梦行 阅读(121) |
评论 (0) |
编辑 收藏
function hel(){
alert("good");
}
Event.observe(document, 'mousedown', hel.bindAsEventListener());
Browser: {
IE: !!(window.attachEvent && !window.opera), //判断是不是IE 转换为boolean
Opera: !!window.opera,
WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
},
posted @
2007-07-03 11:13 华梦行 阅读(142) |
评论 (0) |
编辑 收藏
AnnotationProcessor
Annotation 处理的接口,实现类是DefaltAnnotationProcessor :
protected static void
|
lookupFieldResource
(javax.naming.Context context, java.lang.Object instance, java.lang.reflect.Field field, java.lang.String name)
Inject resources in specified field. |
protected static void
|
lookupMethodResource
(javax.naming.Context context, java.lang.Object instance, java.lang.reflect.Method method, java.lang.String name)
Inject resources in specified method. |
void
|
postConstruct
(java.lang.Object instance)
Call postConstruct method on the specified instance. |
void
|
preDestroy
(java.lang.Object instance)
Call preDestroy method on the specified instance. |
void
|
processAnnotations
(java.lang.Object instance)
Inject resources in specified instance |
public class DefaultAnnotationProcessor implements AnnotationProcessor {
protected javax.naming.Context context = null;
public DefaultAnnotationProcessor(javax.naming.Context context) {
this.context = context;
}
/**
* Call postConstruct method on the specified instance.
*/
public void postConstruct(Object instance)
throws IllegalAccessException, InvocationTargetException {
Method[] methods = instance.getClass().getDeclaredMethods();
Method postConstruct = null;
for (int i = 0; i < methods.length; i++) {
if (methods[i].isAnnotationPresent(PostConstruct.class)) {
if ((postConstruct != null)
|| (methods[i].getParameterTypes().length != 0)
|| (Modifier.isStatic(methods[i].getModifiers()))
|| (methods[i].getExceptionTypes().length > 0)
|| (!methods[i].getReturnType().getName().equals("void"))) {
throw new IllegalArgumentException("Invalid PostConstruct annotation");
}
postConstruct = methods[i];
}
}
// At the end the postconstruct annotated
// method is invoked
if (postConstruct != null) {
boolean accessibility = postConstruct.isAccessible();
postConstruct.setAccessible(true);
postConstruct.invoke(instance);
postConstruct.setAccessible(accessibility);
}
}
/**
* Call preDestroy method on the specified instance.
*/
public void preDestroy(Object instance)
throws IllegalAccessException, InvocationTargetException {
Method[] methods = instance.getClass().getDeclaredMethods();
Method preDestroy = null;
for (int i = 0; i < methods.length; i++) {
if (methods[i].isAnnotationPresent(PreDestroy.class)) {
if ((preDestroy != null)
|| (methods[i].getParameterTypes().length != 0)
|| (Modifier.isStatic(methods[i].getModifiers()))
|| (methods[i].getExceptionTypes().length > 0)
|| (!methods[i].getReturnType().getName().equals("void"))) {
throw new IllegalArgumentException("Invalid PreDestroy annotation");
}
preDestroy = methods[i];
}
}
// At the end the postconstruct annotated
// method is invoked
if (preDestroy != null) {
boolean accessibility = preDestroy.isAccessible();
preDestroy.setAccessible(true);
preDestroy.invoke(instance);
preDestroy.setAccessible(accessibility);
}
}
/**
* Inject resources in specified instance.
*/
public void processAnnotations(Object instance)
throws IllegalAccessException, InvocationTargetException, NamingException {
if (context == null) {
// No resource injection
return;
}
// Initialize fields annotations
Field[] fields = instance.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (fields[i].isAnnotationPresent(Resource.class)) {
Resource annotation = (Resource) fields[i].getAnnotation(Resource.class);
lookupFieldResource(context, instance, fields[i], annotation.name());
}
if (fields[i].isAnnotationPresent(EJB.class)) {
EJB annotation = (EJB) fields[i].getAnnotation(EJB.class);
lookupFieldResource(context, instance, fields[i], annotation.name());
}
if (fields[i].isAnnotationPresent(WebServiceRef.class)) {
WebServiceRef annotation =
(WebServiceRef) fields[i].getAnnotation(WebServiceRef.class);
lookupFieldResource(context, instance, fields[i], annotation.name());
}
if (fields[i].isAnnotationPresent(PersistenceContext.class)) {
PersistenceContext annotation =
(PersistenceContext) fields[i].getAnnotation(PersistenceContext.class);
lookupFieldResource(context, instance, fields[i], annotation.name());
}
if (fields[i].isAnnotationPresent(PersistenceUnit.class)) {
PersistenceUnit annotation =
(PersistenceUnit) fields[i].getAnnotation(PersistenceUnit.class);
lookupFieldResource(context, instance, fields[i], annotation.name());
}
}
// Initialize methods annotations
Method[] methods = instance.getClass().getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].isAnnotationPresent(Resource.class)) {
Resource annotation = (Resource) methods[i].getAnnotation(Resource.class);
lookupMethodResource(context, instance, methods[i], annotation.name());
}
if (methods[i].isAnnotationPresent(EJB.class)) {
EJB annotation = (EJB) methods[i].getAnnotation(EJB.class);
lookupMethodResource(context, instance, methods[i], annotation.name());
}
if (methods[i].isAnnotationPresent(WebServiceRef.class)) {
WebServiceRef annotation =
(WebServiceRef) methods[i].getAnnotation(WebServiceRef.class);
lookupMethodResource(context, instance, methods[i], annotation.name());
}
if (methods[i].isAnnotationPresent(PersistenceContext.class)) {
PersistenceContext annotation =
(PersistenceContext) methods[i].getAnnotation(PersistenceContext.class);
lookupMethodResource(context, instance, methods[i], annotation.name());
}
if (methods[i].isAnnotationPresent(PersistenceUnit.class)) {
PersistenceUnit annotation =
(PersistenceUnit) methods[i].getAnnotation(PersistenceUnit.class);
lookupMethodResource(context, instance, methods[i], annotation.name());
}
}
}
/**
* Inject resources in specified field.
*/
protected static void lookupFieldResource(javax.naming.Context context,
Object instance, Field field, String name)
throws NamingException, IllegalAccessException {
Object lookedupResource = null;
boolean accessibility = false;
if ((name != null) &&
(name.length() > 0)) {
lookedupResource = context.lookup(name);
} else {
lookedupResource = context.lookup(instance.getClass().getName() + "/" + field.getName());
}
accessibility = field.isAccessible();
field.setAccessible(true);
field.set(instance, lookedupResource);
field.setAccessible(accessibility);
}
/**
* Inject resources in specified method.
*/
protected static void lookupMethodResource(javax.naming.Context context,
Object instance, Method method, String name)
throws NamingException, IllegalAccessException, InvocationTargetException {
if (!method.getName().startsWith("set")
|| method.getParameterTypes().length != 1
|| !method.getReturnType().getName().equals("void")) {
throw new IllegalArgumentException("Invalid method resource injection annotation");
}
Object lookedupResource = null;
boolean accessibility = false;
if ((name != null) &&
(name.length() > 0)) {
lookedupResource = context.lookup(name);
} else {
lookedupResource =
context.lookup(instance.getClass().getName() + "/" + method.getName().substring(3));
}
accessibility = method.isAccessible();
method.setAccessible(true);
method.invoke(instance, lookedupResource);
method.setAccessible(accessibility);
}
}
posted @
2007-06-28 15:26 华梦行 阅读(658) |
评论 (0) |
编辑 收藏
http://webfx.eae.net/
posted @
2007-06-28 12:54 华梦行 阅读(116) |
评论 (0) |
编辑 收藏
import java.lang.reflect.*;
public class RunTest {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName("Foo").getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
public class Foo {
@Test public static void m1() {
System.out.println("m1 SUcsessful");
}
public static void m2() { }
@Test public static void m3() {
System.out.println("m3 Fails");
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test public static void m5() { }
public static void m6() { }
@Test public static void m7() {
throw new RuntimeException("Crash"); }
public static void m8() { }
}
import java.lang.annotation.*;
/*
* Test.java
*
* Created on 2007年6月28日, 上午8:52
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
/**
*
* @author ljl
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
posted @
2007-06-28 09:24 华梦行 阅读(297) |
评论 (0) |
编辑 收藏
/** The original page id sent from the server */
dwr.engine._origScriptSessionId = "${scriptSessionId}";
/** The session cookie name */
dwr.engine._sessionCookieName = "${sessionCookieName}"; // JSESSIONID
以上这段代码取自 dwr 源码包中 engine.js
这里的${scriptSessionId} 与 ${sessionCookieName} 是从哪里或得的,又是怎么获得的呢。
有怎么会用 ${} ,${} 是什么意思呢?请各位大侠不吝赐教!
posted @
2007-06-21 21:28 华梦行 阅读(1835) |
评论 (2) |
编辑 收藏