lewang的博客

欢迎来到lewang的博客>>   | 首页 资源中心 | others | others | 一般分类 | ITPUB论坛

四十二天学会英语的模范短文36-42

发表人:lewang | 发表时间: 2006年五月13日, 13:38

第三十六天:TO BE PATIENT 要忍耐

When someone disagrees with you or offends you, don’t lose your temper. Why? Because it is of no use to do so. You ought to (should) be patient and keep calm lest you should quarrel with him. You must know that patience is not cowardice, but a virtue. I hope that everybody practices it.

In addition, patience will also bring us success. When you meet with difficulties in your work, it is no use losing heart. You must keep on fighting until (till) the final victory belongs to you.

当有人和你意见不同或开罪你的时候,你切不可发脾气。为什么?因为这样做是无用的(用of no use)。你应当忍耐并且保持冷静,唯恐和他争吵。你必须知道忍耐不是弱而是一种美德。我希望人人都实行它。

另外,忍耐也会带给我们成功。当你在工作中遭遇到困难的时候,灰心是无用的。你必须继续作战直到最后胜利属于你为止。

--------------------------------------------------------------------------------

第三十七天:SANITARY WAYS IN SUMMER 夏天的卫生方法

Not only is summer a hot season but also diseases are apt to happen. To us it is neither comfortable nor safe.

If we do not wish to get sick, we must pay attention to the following sanitary ways in summer.

Both fresh air and clean food are indispensable to us.

We must try our best to get (obtain) them.

We should take at least one bath every day.

Don’t wear dirty clothes.

In conclusion, if we can carry out the above – mentioned rules, we will neither get (take/fall) sick nor suffer pain.

夏天不仅是个炎热的季节,而且疾病也容易发生。它对我们既不舒适也不安全。我们希望不生病,就得注意下面那些夏天卫生的方法。

新鲜空气和干净的食物两者对我们是不可缺少的。我们必须尽全力去获得它们。

我们每天至少应当洗一个澡。

不要穿脏衣服。

总而言之,如果我们能实行上面所说的那些规则,我们既不会生病也不会吃苦。

--------------------------------------------------------------------------------

第三十八天:WORK WHILE YOU WORK, PLAY WHILE YOU PLAY 工作时工作,游玩时游玩

Both work and play are necessary to us; the former gives us knowledge while the latter (gives) rest. An English proverb is well said: “Work while you work play while you play.” It makes our life pleasant, efficient and successful.

Work is one thing and play is another. It is of course not good to work all day long. However, it is also not good to play all day long. While you work, you should work in earnest. Then while you play, you will feel more relaxed and pleasant. That goes without saying.

工作和游玩两者对我们都是必须的,前者给我们知识,后者给我们休息。英谚说得好“工作时工作,游玩时游玩”。它使我们的生活愉快,有效率以及成功。

工作是一回事,游玩又是另一回事。整日工作自然不好。然而,整天游玩也是不好。当你工作时,你应认真工作。那末当你游玩时,你会觉得比较轻松愉快。那是不需说的。

--------------------------------------------------------------------------------

第三十九天:MY FAVORITE PASTIME 我心爱的消遣

It goes without saying that work without rest will do harm to health. In other words, we had better take sufficient recreation to relax after work.

There is no doubt that there are a number of amusements, such as playing ball games, collecting stamps, fishing, gardening, skating and so on.

As for (to) me, I take great delight in gardening. Whenever I am at leisure, I am accustomed to growing flowers. I regard them as my most agreeable companion.

不需说,工作而不休息会对健康有害的。换句话说,我们最好工作后,从事分的消遣来轻松一下。

无疑的,消遣的种类很多,诸如打球,集邮,钓鱼,园艺,溜冰等等。

就我而言,我大大喜欢园艺。每当我有闲暇的时候,我习惯于种花。我把它们看作是我最称心如意的伴侣。

--------------------------------------------------------------------------------

第四十天:FACTORS OF SUCCESS 成功要素

I would rather suffer hardships than fail. In other words, to achieve success is my only desire. We all know that there are many factors of success. Now let me write down the most important ones in the following.

Diligence—Foolish as a person is, he can succeed in his work if he works hard. That does not admit of any doubt.

Perseverance—When you fail, don’t lose heart. Keep on working until you accomplish your aim.

Honesty—If you tell lies or does not keep your words, no one will rely on you. I would rather be scolded than cheat (others).

我宁愿受苦难不愿失败。换句话说,获致成功是我唯一的愿望。我们大家知道成功要素很多。现在让我们把它们最重要的写在下面。

勤勉――一个人虽然不大聪明,但是他地能做成他的工作,如果他苦干的话。那是不容置疑的。

毅力――当你失败的时候,不要灰心。要继续工作,直到你达到目的为止。

诚实――如果你说谎或不守信,没有人会信赖你。我宁愿挨骂不愿欺骗人。

--------------------------------------------------------------------------------

第四十一天:MY AIM 我的志向

Whatever a man’s status is (may be), he must have an aim. If not, he can hardly stand on his own two feet in the world. That stands to reason.

However important fame and wealth may be, we must not let them become our object in life. We should aim at doing something useful to the society.

If my aim can come true, I hope (wish) to be a teacher. However, teaching is by no means an easy thing. I must apply myself closely to my studies so as to be able to cope with my duties (as a teacher).

不论人是什么身份,他必须有一个志向。如果没有的话,他很难立足于世上。那是显而易见的。

不论名利如何重要,我们切不可让它们成为我们生活之目标。我们应当志在做一些对社会有益的事情。

如果我的志向能实现的话,我就希望成为一个老师。然而,教书并不是一容易的事情。我必须专心学业以便能应因我的职责。

--------------------------------------------------------------------------------

第四十二天:OUR NATIONAL FATHER 我的國父

There is no doubt that Dr. Sun Yat-Sen is the greatest man that I ever known in my whole life. He set up the Republic of China, so we call him our National Father.

He was devoted to the revolution about forty years. His aim was to liberate China from the hands of the Manchu Dynasty. He had not met with result, but he was not discouraged at all. As a result, he accomplished his end in the long run.

To sum up, his strong will and indomitable spirit are worth our worship and imitation (are worth our worshiping and imitating).

无疑的,中山先生是我一生知道的最大的人物。他建立中民國,所以我他為我的國父。

他致力革命凡四十年。他的目的是清朝手里解放中國。他曾遭遇屡次的失败,但他却一点也不灰心。那是不需说的。结果,他终于达到目的。

总而言之,他坚强的意志和不屈不挠的精神(是)值得我们的崇拜和仿效。


四十二天学会英语的模范短文31-35

发表人:lewang | 发表时间: 2006年五月13日, 13:36

第三十一天:THRIFT 节俭

There is not a (no) thrifty man but becomes a rich man sooner or later. Why? Because he will not spend such money as is unnecessary. Little by little his money will accumulate. Ten to one, he is bound to make a fortune.

I do not like such men as spend their money in a wrong way. They do not know extravagance is a bad thing. It can only make them happy for the time being. In short, thrift makes poor men rich and extravagance makes rich men poor.

没有一个节俭的人迟早不成为一个富翁。为什么?因为他决不花像那种不必要的钱。渐渐他的钱就会聚积起来。十之八九,他一定会发财的。

我不喜欢那种乱花钱的人。他们不知道奢侈是一件坏事。它只能够使他们暂时快乐而已。总而言之,节俭使穷人变富,奢侈使富人变穷。

--------------------------------------------------------------------------------

第三十二天:PERSEVERANCE 毅力/坚忍

Needless to say, nothing but perseverance can lead a man to the way of success. In other words, a persevering man never does his work without succeeding in it. This is indeed unchangeable truth.

Our National Father, Dr. Sun Yat-Sen, is the most ideal example. He was devoted to the revolution about forty years. He met with many failures, but he was anything but discouraged. As a result, he won. The Republic of China was born.

不需说,只有毅力才能引导人迈向成功之途。换句话说,一个具有毅力的人做工作终是(never...without)会把它做成功的。这真是不易之定理也。

我们国父孙中山先生是一个最理想的例了.致力国民革命凡四十年。他遭遇到许多次的失败,但是他决不灰心。结果,他赢了。中华民国诞生了。

--------------------------------------------------------------------------------

第三十三天:MY MOTHER 我母亲

My mother is a woman of the old school. She is too conservative to keep up with the times. However, she is good-natured and treats others incerely. For this reason, all her neighbors are only too glad (pleased) to make friends with her. They consider her a model woman.

She is a typical housewife. She keeps the (her) house neat and clean and looks after us with extreme care . She often says to us, “You cannot study too ard.”

我母亲是中国老式女子。她太保守无法跟上时代。然而,她性情善良,待人诚恳。基于此种理由,所有她的邻居都十分高兴和她结交朋友。他们认为她是模范女子。

她是一位标准的家庭主妇。她把家保持整洁。她小心翼翼地照顾我们。她时常对我们说:“你们越用功越好”。

--------------------------------------------------------------------------------

第三十四天:WHERE THERE IS A WILL, THERE IS A WAY 有志者事竟成

The secret of success (The key to success) is not so much money as a strong will. A great man is one who has a strong will and an indomitable spirit. In other words, if a man does not have a strong will to win (get) the final victory, he will never succeed in his life. He is no more than a failure.

It is quite obvious that there is no difficult thing (nothing difficult) in the world. if you make up your mind to do it, you will certainly accomplish your end. That stands to reason.

成功的要诀不是金钱而是一个坚强的意志(用not so mush... as)。一个大人物是一个具有坚强意志和不屈不挠精神的人。换句话说,如果一个人没有坚强意志去获得最后胜利的人,他终其一生永远不会成功。他只不过是(用no more than)一个失败者。

很显明的世界上并没有难事。如果你下定决心去做它,你一定会达到目的。那是显而易见的。

--------------------------------------------------------------------------------

第三十五天:DUTIES OF A STUDENT 学生的责任

Education is the very thing that we want to receive. Our parents send us to school so as to enable us to get (obtain) knowledge and achieve great things in the future. The following are the duties of a student (which) we should keep in mind.

In the first place, we should be filial to our parents and respectful to our teacher.

In the second place, we have to (must )study as hard as we can.

In the third place, we must not tell lies.

Last of all, we must not criticize others.

To sum up, the above-mentioned rules are the very duties of a student.

教育就是我们要接受的东西。我们父母送我们上学以便能使人们获得知识与将来成大事。下面是我们应该记住的学生的责任。

我们应对父母要孝顺,对老师要尊敬。

我们要尽可能的用功读书。

我们切不可说谎。

最后,我们不要批评别人。

总而言之,上面所说的规则就是我们应尽的责任。


四十二天学会英语的模范短文31-35

发表人:lewang | 发表时间: 2006年五月13日, 13:36

第三十一天:THRIFT 节俭

There is not a (no) thrifty man but becomes a rich man sooner or later. Why? Because he will not spend such money as is unnecessary. Little by little his money will accumulate. Ten to one, he is bound to make a fortune.

I do not like such men as spend their money in a wrong way. They do not know extravagance is a bad thing. It can only make them happy for the time being. In short, thrift makes poor men rich and extravagance makes rich men poor.

没有一个节俭的人迟早不成为一个富翁。为什么?因为他决不花像那种不必要的钱。渐渐他的钱就会聚积起来。十之八九,他一定会发财的。

我不喜欢那种乱花钱的人。他们不知道奢侈是一件坏事。它只能够使他们暂时快乐而已。总而言之,节俭使穷人变富,奢侈使富人变穷。

--------------------------------------------------------------------------------

第三十二天:PERSEVERANCE 毅力/坚忍

Needless to say, nothing but perseverance can lead a man to the way of success. In other words, a persevering man never does his work without succeeding in it. This is indeed unchangeable truth.

Our National Father, Dr. Sun Yat-Sen, is the most ideal example. He was devoted to the revolution about forty years. He met with many failures, but he was anything but discouraged. As a result, he won. The Republic of China was born.

不需说,只有毅力才能引导人迈向成功之途。换句话说,一个具有毅力的人做工作终是(never...without)会把它做成功的。这真是不易之定理也。

我们国父孙中山先生是一个最理想的例了.致力国民革命凡四十年。他遭遇到许多次的失败,但是他决不灰心。结果,他赢了。中华民国诞生了。

--------------------------------------------------------------------------------

第三十三天:MY MOTHER 我母亲

My mother is a woman of the old school. She is too conservative to keep up with the times. However, she is good-natured and treats others incerely. For this reason, all her neighbors are only too glad (pleased) to make friends with her. They consider her a model woman.

She is a typical housewife. She keeps the (her) house neat and clean and looks after us with extreme care . She often says to us, “You cannot study too ard.”

我母亲是中国老式女子。她太保守无法跟上时代。然而,她性情善良,待人诚恳。基于此种理由,所有她的邻居都十分高兴和她结交朋友。他们认为她是模范女子。

她是一位标准的家庭主妇。她把家保持整洁。她小心翼翼地照顾我们。她时常对我们说:“你们越用功越好”。

--------------------------------------------------------------------------------

第三十四天:WHERE THERE IS A WILL, THERE IS A WAY 有志者事竟成

The secret of success (The key to success) is not so much money as a strong will. A great man is one who has a strong will and an indomitable spirit. In other words, if a man does not have a strong will to win (get) the final victory, he will never succeed in his life. He is no more than a failure.

It is quite obvious that there is no difficult thing (nothing difficult) in the world. if you make up your mind to do it, you will certainly accomplish your end. That stands to reason.

成功的要诀不是金钱而是一个坚强的意志(用not so mush... as)。一个大人物是一个具有坚强意志和不屈不挠精神的人。换句话说,如果一个人没有坚强意志去获得最后胜利的人,他终其一生永远不会成功。他只不过是(用no more than)一个失败者。

很显明的世界上并没有难事。如果你下定决心去做它,你一定会达到目的。那是显而易见的。

--------------------------------------------------------------------------------

第三十五天:DUTIES OF A STUDENT 学生的责任

Education is the very thing that we want to receive. Our parents send us to school so as to enable us to get (obtain) knowledge and achieve great things in the future. The following are the duties of a student (which) we should keep in mind.

In the first place, we should be filial to our parents and respectful to our teacher.

In the second place, we have to (must )study as hard as we can.

In the third place, we must not tell lies.

Last of all, we must not criticize others.

To sum up, the above-mentioned rules are the very duties of a student.

教育就是我们要接受的东西。我们父母送我们上学以便能使人们获得知识与将来成大事。下面是我们应该记住的学生的责任。

我们应对父母要孝顺,对老师要尊敬。

我们要尽可能的用功读书。

我们切不可说谎。

最后,我们不要批评别人。

总而言之,上面所说的规则就是我们应尽的责任。


四十二天学会英语的模范短文26-30

发表人:lewang | 发表时间: 2006年五月13日, 13:34

第二十六天:HOW I SPENT MY SUMMER VACATION 我如何度过暑假

No sooner had the summer vacation begun than I returned to my native town. Of course I must make good use of it; otherwise I would incur the displeasure of my parents.

In the morning I reviewed my lessons and read newspapers or magazines. In the afternoon I played ball games with my friends or went fishing in the river. At night (In the night) I watched television with my family (the members of my family). hardly had the clock on the wall struck ten when I went to bed.

暑假刚一开始,我就回故乡了。自然我须好好利用它,否则我会招致父母的不快。

早晨我复习功课并阅读报纸或杂志。下午我和朋友打球,或去河中钓鱼。晚上我就和家人看看电视。墙上的钟刚敲十下,我就去睡觉了。

--------------------------------------------------------------------------------

第二十七天:KNOWLEDGE IS POWER 知识就是力量

If we have no knowledge, we cannot succeed in doing any work. Why? Because knowledge is power. With knowledge we have conquered nature and invented steamers, trains and airplanes. We can send messages by telegram. We can talk with our friends by telephone.

As is well known, we students are the future masters of the nation. She (It) needs us very much. If we do not make efforts to acquire knowledge, how can we render service to her (it)?

如果我们没有知识,我们就不能做成功任何事情。为什么?因为知识就是力量。冯藉知识我们征服了大自然,并且发明了轮船,火车与飞机。我们能用电报传递消息。我们能用电话和朋友谈话。

如众所周知,我们学生都是国家未来的主人翁。她非常需要我们。如果我们不努力去获得知识,将来如何来报效她呢?

--------------------------------------------------------------------------------

第二十八天:A LETTER TO A FRIEND 一封给友人的信

Dear X:

We have not seen each other for a long time. I miss (think of) you very much. I wish (that) I had wings and could fly to your home. However, this is impossible!

Father (My father) has gone abroad of late. If I had had time then, I would have accompanied him. Only Mother (my mother) and I are at home now (at present). We are leading (living) a quiet and happy life. We look as if we were fairies. I hope (that) I can hear from you very often.

Sincerely yours.

亲爱的××:

我们彼此有好久没有见面了。我十分想我。我希望我有双翅膀而且能够飞到你家去。然而,这是不可能的!

我父亲最近出国了。我那时如果有时间的话,我就会陪着他去。现在家里只有我母亲和我。我们过着一个很宁静而快乐的生活。我们看起来好像神仙似的。希望我能常常接到你的信。

祝你好

××× 敬上

--------------------------------------------------------------------------------

第二十九天:CHOOSING FRIENDS 选择朋友

A man who is careful in choosing friends will certainly derive benefit from them. Why? Because there are more false friends than real ones in this world. To have one real friends is better than a hundred false ones. Friends who flatter you to your face are not true friends.

We must make friends with those who have good character and kindness of heart. On the other hand, we should do our best to a void keeping company with bad people.

一个小心选择朋友的人一定会从他们那里获得益处。为什么?因为在这个世界里假朋友比真朋友要多。有一个真朋友要比有一百个假朋友来得好。凡是当面奉承你的朋友不是真朋友。

我们必须和凡是具有良好品格和心地善良的人交朋友。反过来说,我们应尽力避免结交坏人。

--------------------------------------------------------------------------------

第三十天:TO RISE EARLY 论早起

I do not know the reason why some people want to get up late. They will never have the opportunity to enjoy (of enjoying) the fresh air and calmness of the morning. This is indeed a quite regrettable thing.

To rise early is a good habit (which) we should cultivate. Why? Because the best time when we can pursue our studies is in the morning. In addition, early rising is also good to our health. I hope that everybody our knows the reason why we must rise early.

我不知道某些人要晚起的理由。他们永不会有机会来享受早晨的新鲜空气和宁静。这真是一件 发令人遗憾的事情。

早起是我们应该养成的一种良好习惯。为什么?因为早晨是我们从事学业的大好时间。再者,早起对我们健康也有益处。我希望每个人应该知道我们必须早起的理由。


四十二天学会英语的模范短文21-25

发表人:lewang | 发表时间: 2006年五月13日, 13:32

第二十一天:WAR 战争

Though the people in the world are all opposed to war, they can by no means avoid it. This is indeed a regrettable thing.

War is very terrible. Everybody hears it with fear. In a war thousands and thousands of lives will be lost and a large amount of money (will be) wasted. It brings us untold miseries and damage.

To sum up, so long as war is unavoidable, human beings cannot enjoy lasting peace. How tragical their fate is!

虽然世界的人民都反对战争,但是他们却无法能够避免它。这确实是件非常令人遗憾的事情。

战争是非常可怕的。每个人听到它都害怕。在一次战争中,成千成万的人丧失了生命,大量金钱浪费了。它带给我们无穷尽的痛苦和损失。

总而言之,只要战争不可避免,人类不可能享受持久的和平。他们的命运是多么悲惨呀!

--------------------------------------------------------------------------------

第二十二天:COOD HEALTH 良好的健康

We all wish (hope) to be happy, so we should take good care of our health.

Health is the best treasure (which) a man can possess. Money can do many things, but it cannot buy happiness. However, so long as man has good health, he can enjoy the pleasures of human life.

In order to insure good health we must pay attention to three things. They are-nourishing food, fresh air and proper exercise.

我们大家都希望快乐,所以我们应该好好保重健康。一个病人因为失去健康而很少快乐。

健康是一个人所能拥有最好的财富。钱能做许多事情,但是它却不能购买。然而,只要一个人有良好的健康,他就能享受人生的乐趣。

为了保证良好的健康,我们必须注意三件事情。它们是营养食物,新鲜空气和正当运动。

--------------------------------------------------------------------------------

第二十三天:MONEY 钱

Everybody wishes (hopes) to get money so that he can maintain his livelihood. In other words, money is so useful that it is impossible for mankind to live without it. For this reason, we have an interesting proverb. It says “Money can make the world go around.” In a word, money is more powerful than anything else.

However, money should not be wasted. It must be used for (some) proper purposes. We young men should form the good habit of not spending money in the wrong way.

每个人为了能够维持生活,所以都希望得到钱。换句话说,钱是非常有用,所以没有它人类即无法生活。基于此种理由,我们有一句有趣的谚语。它说有“钱能使鬼推磨”。一言以蔽之,钱比任何另外的东西更有力量。

然而,钱不应该被浪费掉。它必须用于正当的用途上。我们青年人应该养成不乱花钱的好习惯。

--------------------------------------------------------------------------------

第二十四天:THE CHINESE NEW YEAR 中国新年

I like the Chinese new year better than any other festival. This is a time especially for rest and joy. I need not study. I wear good clothes and eat good food. I have a good time from morning till night. To be frank with you, I am as happy as a king.

The Chinese new year lasts as long as fifteen days. It gives us more pleasure than we have imagined. After that we have to(比用must来得好)resume our normal work.

我喜欢中国新年比喜欢其它任何节日更甚。这是一个专为休息和欢乐的时间。我不需要读书。我穿好衣服,吃好东西。我每天从早到晚日子过得轻松愉快。坦白地说,我和帝一样快乐。

中国新年持续十五天之久,它给我们的欢乐比我们想像中的更多。之后,我们就得恢复我们正常的工作了。

--------------------------------------------------------------------------------

第二十五天:THE ZOO 动物园

The zoo is the most interesting place that I have ever seen in my whole life. It is like a garden which shows (exhibits) various kinds of animals, birds and insects.

It is indeed a good idea to pay a visit to the zoo during school holidays. The more you look at the animals, the happier you will become. Of (Among) all the animals, the monkey is (monkeys are) the funniest. That’s my opinion. Do you agree.

动物园是我一生中所知道的最有趣的地方。它像一个各种类类动物,鸟类以及昆虫展览的花园。

在学校放假期间,参观一下动物园,确实是一个好主意。你越喜欢看动物,你将越变得快乐。在所有动物中,猴子是最滑稽的。那是我的意见。你同意么?


四十二天学会英语的模范短文16-20

发表人:lewang | 发表时间: 2006年五月13日, 13:32

第十六天:THE NATIONAL FLAG 国旗

it is the duty of every citizen to honor the national flag. Why? Because the national flag is the symbol of a (the) country. To respect it means to respect the country. In other words, if a man loves his country, he must love the national flag.

In school, the national flag is usually raised at a certain time every day. Then the principal, teachers, staff and students are to (must) stand before it and sing the national anthem. It is indeed extremely meaningful to attend such a ceremony.

尊敬国旗是每个公民的责任(义务)。为什么?因为国旗是国家的象征。尊敬它意思就是尊敬国家。换句话说,如果一个人爱国,他必须爱国旗。

在学校里,通常每天定时要升旗。那时,校长,老师,教职员学生教师将恭敬地站在它前面并且唱国歌。参加此项仪式确实非常有意义。

--------------------------------------------------------------------------------

第十七天:THE IMPORTANCE OF EDUCATION 教育的重要性

It is impossible for us to make our country rich and strong without developing education. Why? Because education gives people knowledge and teaches them how to become good citizens so as to be able to serve their country. No wonder they say that education decides the progress, prosperity and civilization of a country.

At present (Nowadays) most countries in (of) the world are enforcing compulsory education. It is necessary for all kinds of people, both rich and poor, to receive education. Taiwan is an exception as well not.

我们不可能使我们的国家富强而不发展教育。为什么?因为教育给予人民知识并教他们如何成为好公民以便能为国家效力。难怪,有人说教育决定一个国家的进步,繁荣与文化。

现在世界大多数的国家都在实施强迫教育。所有各类人民不论贫富都必须接受教育。台湾也不例外。

--------------------------------------------------------------------------------

第十八天:INDUSTRY (DILIGENCE) 勤勉

It is a matter of course that industry will bring us success, wealth and good luck. I am sure that a hard-working person can always succeed in the work which he wants to do. This is unchangeable truth.

Idleness is the opposite of industry. It is the source of all evil. An idle man only enjoys playing and making pleasures. That he is doomed to failure is of no doubt. We should not follow his example.

勤勉会带给我们成功,财富和好运乃是当然之事。我确信一个苦干的人终是能够做成功他所要做的工作。这是不易之定理。

懒惰是勤勉的反面。它是万恶之源泉。一个懒惰的人只享受玩耍和寻乐。他命运注定失败是毫无疑问的。我们不应学他的榜样。

----------------------------------------------------------------------------

第十九天:MY NATIVE TOWN 我的故乡

My native town is X. We have settled down here since my grandfather was a child. In other words, my family has lived here for more than one hundred years.

It is a small village. There are about one thousand inhabitants. Most of them are farmers. The mode of their living is very simple. However, they have already possessed television sets and refrigerators. They made up their minds to live a modern life.

我的故乡是X。我们自从祖父是小孩时,就定居在此地。换句话说,我们家人在此地已经住了一百余年了。

这个小村庄。居民大约有一千人。他们大部分是农夫。他们的生活方式很简单。然而,他们已拥有电视和冰箱。他们下定决心要过一个现代的生活。

--------------------------------------------------------------------------------

第二十天:MY SCHOOL LIFE 我的学校生活

When I was six years old, I began to go to school. The first school (which) I attended was a primary school. There were many subjects (which were taught in the school, such as Chinese, arithmetic, history, geography, drawing and so forth. Since I was (became) a student, I studied very hard. My parents were quite proud of me.

After I had studied there for three years, I entered a junior high school. When I was twelve years old, I became a student of a senior high school. I still studied very hard. Except on sick leave I was never absent from class. Everybody looked upon me as a model student.

我六岁的时候,就开始上学了。我第一个上的学校是一所国小。学校里教的课目有许多,诸如国文,算术,史地,画图等等。既然我是一个学生,我就用功读书。我父母深以我为荣。

我在那边读了三年后,就进入初中。我十二岁的时候,就成为一个高中的物理学。我依旧用功读书。除病假外,我永不缺课。人人都把我看作是一个模范生。


四十二天学会英语的模范短文6-10

发表人:lewang | 发表时间: 2006年五月13日, 13:31

第六天:A TRIP TO THE COUNTRY 乡村游记

One Sunday my mother (Mother) had (made) me take my little young brother to the a trip to the country. She bade me take good care of him.

While we were walking along the road, the sun was shining brightly and the breeze was blowing gently. We saw the beautiful flowers smile (smiling) at us and heard the birds sing (singing) their sweet songs on the trees. The scenery was indeed very pretty (beautiful).

When we felt tired, we returned home. We saw Mother (our mother) wait (waiting) for us at the door.

有一个星期日,我母亲叫我带小弟弟去乡村游历。她吩咐我要好好照料他。

当我们沿着道路行走的时候,太阳灿烂地照耀着,微风轻轻地吹着。我们看见美丽的花儿对我们微笑着,并听见鸟儿在树上唱着悦耳的歌曲,风景实十分美丽。

当我们感觉到疲倦的时候,我们就回家了。我们看见母样正在门口等候我们。

--------------------------------------------------------------------------------

第七天:BE PATRIOTIC 要爱国

It is the duty of every citizen to make the country rich and powerful (To make the country rich and strong is...). In order to accomplish this object one must be patriotic (love his country). I consider this an unchangeable truth.

How can a student love his country (be patriotic)? I find my answer very simple and clear. He must study hard and store up knowledge so as to serve his (the) country in the future. If every student can do according to what I said, the country will certainly be rich and powerful.

要使国家富强是每个公民的责任。为了达到此目的,必须爱国。我认为这是一条不易的定理。

一个学生如何才能爱国呢?我发觉答复很简单明了。他必须用功读书并积储知识以便将来服务国家。如果每个学生能按照我所说的去做,国家一定会富强。

--------------------------------------------------------------------------------

第八天:THE VALUE OF TIME 爱惜时光

An English proverb says that time is money. I consider it (this) wrong. Why? Because we all know that we can earn money be work but can not in any way get back time (in anyway). For this reason, we may (can) say that time is more valuable than money.

Many people do not know the value of time. It (this) is indeed a great pity. We must bear (keep) in mind that wasting time is equal to wasting your life.

英国有句谚语说,时间就是金钱。我认为这是不对的。为什么?因为我们大家都知道我们能够用工作赚钱,但无论如何却无法把时间争取回来。基于此种理由,我们可以说时间比钱钱更宝贵。

许多人不知爱惜时光。这确实是可惜的。我们必须记住浪费时间等于浪费生命。

--------------------------------------------------------------------------------

第九天:WHY SHOULD WE STUDY ENGLISH 为什么我们要学英文

If you want to ask me why we should study English, my answer will be simple and clear. Now let me enumerate the reasons one by one in the following.

In the first place, English has become an international language. If you know English, you van make a trip round the world without being misunderstood.

In the second place, most valuable books, newspapers and magazines are written in English. If you wish (hope) to get knowledge, you must learn English.

如果你要问我们为什么我们要学英文。我的答复很简单明了。现在让我来把我的理由一一列举在下面:

英文已成为一种国际语言。如果你通晓它,你可以环游世界不会被人误解。

大多数有价值的书籍,报纸和杂志都是用英文写的。如果你希望获得知识,你必须学习英文。

--------------------------------------------------------------------------------

第十天:MY BIRTHDAY 我的生日

Yesterday was my birthday, so some of my classmates sent me presents. Mother prepared a tea party for me. I invited all of them to come and take part in it.

The tea party began at half past six. There were cold drinks and refreshments. We ate, talked and laughed. We felt that we were the happiest men in the world.

Time passed quickly. In a twinkling, the clock on the wall struck nine. We could not but say “Good-bye” to one another.

昨天是我生日,所以有几位我的同班同学送我礼。母亲给我准备一个茶会。我邀请他们都前来参加。

茶会下午六点半开始。有冷饮和点心。我们又吃又谈又笑。我们觉得是世界上最快乐的人。

时间过得很快。转瞬间,墙上的钟敲九点了。我们不得不互道再见。


四十二天学会英语的模范短文11-15

发表人:lewang | 发表时间: 2006年五月13日, 13:31

第十一天:HOW TO SPEND SUNDAYS 如何消度星期日

Some of us always spend Sundays aimlessly. We can seldom derive profits from the valuable hours (time) of Sundays. This is indeed a great pity. Though Sundays are set aside as a day for rest, we must make good use of them.

We know that our school lessons are usually not sufficient. We should often utilize Sundays to read reference books. In this way we will increase our knowledge. Besides, we must be engaged in sports or outing so as to strengthen our bodies.

我们许多人终是把星期日毫无目的地消度掉。这确实是十分可惜。我们很少能从星期日的宝贵时光中获得益处。虽然星期日是被指定为一个休息的日子,但是我们却应好好利用它。

我们知道我们学校的功课通常是不够的。我们应时常利用星期日来阅读参考书。如此我们的知识就会增加了。此外,我们必须从事运动或郊游以便强壮我们的身体。

--------------------------------------------------------------------------------

第十二天:HONESTY IS THE BEST POLICY 诚为上策

There is an English proverb which says (saying) “Honesty is the best policy.” It signifies the importance of honesty.

What are the benefits of honesty? If you are honest to others, they will be honest to you in return. When you are sad, they will comfort you. When you are in trouble, they will help you.

There is no enumerating the evils of dishonesty here. In a word, a dishonest man will be looked down upon by others and (be) regarded as a public enemy.

英国有一句谚语说“诚为上策”。它说明了诚的重要性。

诚的益处是什么?如果你对他人诚实,他们也会对你诚实作为报答。当你忧悉的时候,他们会安慰你。当你是处在困难中的时候,他们会帮助你。

无法在这里数述不诚实的害处。一言以蔽之,一个不诚实的人会被人看轻的而且被看作为一个公敌。

--------------------------------------------------------------------------------

第十三天:HOW EXERCISE HELPS 运动的利益

If our bodies are not strong, our spirit to do things will certainly be quite dull. And at the same time, we are lack of energy (ies) to study. Diseases will only attack the weak, but not the strong.

Why are our bodies not strong? Because we do not pay attention to exercise. For this reason, we students must often take different kinds (sorts) of exercise in the gym.

Exercise helps us (to) strengthen our bodies and avoid disease. It also teaches us cooperation, for most exercise is played by team.

如果我们的身体不强壮,我们做事的精神一定会十分迟钝。而且同时,我们也缺少足够的精力来读书。疾病只为侵击体弱的人而不是强壮的人。

为什么我们的身体会不强壮呢?因为我们不注意运动。基于此种理由,我们学生必须时常在运动场做不同种类的运动。

运动帮助我们强壮身体并避免疾病。它也教我们合作,因为大多数的运动都是团体游玩的。

--------------------------------------------------------------------------------

第十四天:THE BENEFITS OF TRAVELING 旅行的益处

I am always interested in traveling. My reasons are quite (extremely) simple and clear. If anybody is not satisfied with my viewpoint(s), I shall be greatly surprised at his ways of thinking.

In the first place, traveling increases our knowledge. Only by traveling can we see (因为用(only)开头,主动词须颠倒)things outside our home town.

In the second place, traveling is good to our health. While we are traveling, we usually exercise our bodies.

In conclusion, I earnestly hope that everybody must seize (grasp at) the opportunity of traveling.

我对旅行终是感觉有趣。我的理由非常简单时了。如果任何人对我的观点不满意的话,我将对他的想法会大大吃惊。

旅行增加我们的知识。只有藉旅行我们可以看到我们故乡以外的东西。

旅行对我们的健康有益。当我们旅行的时候,我们通常运动我们的身体。

--------------------------------------------------------------------------------

第十五天:HOW TO BE A GOOD CITIZEN 如何做一个好公民

My aim is to become a good citizen so as to be able to render service to the (my) country. However, to become a good citizen is not an easy thing. He has many duties to fulfill.

The first duty of a good citizen is to love his country. He is to (must) be ready to sacrifice even his own life for the country.

His second duty is to obey the law and help the government (to) maintain order. If everybody can do so, the country will be rich and strong (powerful).

我的志向是做一个好公民以便能给国家效力。然而,做一个好公民不是一件容易的事情。他有许多责任要尽。

一个好公民第一件责任是爱他的国家。他须准备为国家牺牲自己的生命。

他第二件责任是服从法律并且帮助政府维持治安。如果人人都能这样做的话,国家必定富强。


四十二天学会英语的模范短文1-5

发表人:lewang | 发表时间: 2006年五月13日, 13:30

第一天:THE NEWSPAPER 报 纸

Nowadays the newspaper possesses considerable value Everybody should read it. It supplies us with a variety of news every day. It tells us the political situation of the world. If we form the habit of reading the newspaper, we shall (will) get enough knowledge to cope with our circumstances.

现今报纸拥有极大的价值,人人都应该看它。它每天提供我们各种类类的消息。它告诉我们世界政治局势。如果我们养成看报的习惯,我们就能得到足够的知识来因应我们的环境。

学生虽然每天须做功课,但他们至少应该匀出一两个小时来看报。哪些,他们不但能增加知识而且也能赶上时代。总而言之,看报对学生很有益处。

--------------------------------------------------------------------------------

第二天:MY DAILY LIFE 我的日常生活

Though my daily life is extremely monotonous, I try hard to adapt myself to it. Why? Because I intend to be a good student. I wish to render service to my country.

I get up at six o’clock every day. After I wash my face and brush my teeth, I begin to review my lessons. I go to school at seven o’clock.

After school is over, I return home. We usually have supper at seven o’clock.

Then I begin to do my homework. I want to finish it before I go to bed.

虽然我的日常生活十分单调,但我却竭力设法去适应它。为什么?因为我打算做一个好学生,希望将来为国家服务。

我每天六点起床、洗脸刷牙后,就开始复习功课,七点钟我就去上学。

放学后,我就回家了。我们通常在七点钟吃晚餐,之后我就开始做家庭作业,希望在睡觉前把它做完。

--------------------------------------------------------------------------------

第三天:A MODEL STUDENT 模范学生

Do you mind being called a bad student? Of course not. So far as I know, everybody intends to be (become) a model student.

However, to be a model student is by no means an easy thing. First, he must do his best to obtain knowledge. A man without sufficient knowledge will not succeed. Secondly, he must remember to improve his health. Only a strong man can do great tasks. Thirdly, he should receive moral education. If his conduct is not good, no one will consider making friends with him.

你价意被称为坏学生吗?当然不。就我所知,每个人都打算做模范学生。

然而,做模范学生却不容易。第一,他必须尽力获得知识(求知)。一个没有足够知识的人是不会成功的。第二,他必须记住促进健康。只有强壮的人才能做大事。第三,他应该接受道德教育。如果他品行不好,没有人会考虑和他交朋友的。

--------------------------------------------------------------------------------

第四天:HOW TO GET HAPPINESS 如何获得快乐

There is no doubt that happiness is the most precious thing in the world. Without it, life will be empty and meaningless. If you wish to know how to get happiness, you must pay attention to the following two points.

First, health is the secret of happiness (the key to happiness). Only a strong man can enjoy the pleasure of life.

Secondly, happiness consists in contentment. A man who is dissatisfied with his present condition is always in distress.

无疑的快乐是世界上最宝贵的东西。没有它,人生将是空虚的而且毫无意义的。如果你希望知道如何获得快乐,你须注意下面两点。

健康是快乐的要诀。唯有身体强壮的人才能享受人生的乐趣。

快乐在于知足。一个不满于现状的人终是处在痛苦之中。

--------------------------------------------------------------------------------

第五天:BOOKS 书籍

As is well known, books teach us to learn life, truth, science and many other useful things. They increase our knowledge, broaden our minds and strengthen our character. In other words, they are our good teachers and wise friends. This is the reason why our parents always encourage us to read more books.

Reading is a good thing, but we must pay great attention to the choice of books. It is true that we can derive benefits from good books. However, bad books will do us more harm than good.

如众所周知,书籍教我们学习人生,真理,科学以及其它许多有用的东西。它们增加我们的知识,扩大我们的心胸并加强我们的品格。换句话说,它们是我们的良师益友。这是为什么我们的父母终是鼓励我们要多读书的理由。

读书是一好事,但我们必须多加注意书的选择。不错,我们能从好书中获得益处。然而,坏书却对我们有害无益。


Jsp + JavaBean循序渐进教程(四)下

发表人:lewang | 发表时间: 2006年五月13日, 13:29

<font color="#0000FF">adduser.java文件

  说明:主要进行用户数据的读取以及添加操作,从db派生出来,addNewUser方法用来进行用户数据的添加,checkUser()方法用来检查用户名是否重复,另外还有一些set/get方法用来对属性进行处理,dousernew.jsp文件将进行条用。

  // Copyright (c) 2000 http://jspbbs.yeah.net
package lyf;
/**
* A Class class.
* $#@60;P$#@62;
* @author liuyufeng
*/
  //导入java类库
import java.sql.*;
import java.lang.*;
import oracle.jdbc.driver.*;
  //adduser由db派生出来,拥有db的成员变量和方法
public class adduser extends db {
  //构建器
public boolean addNewUser(){
boolean boadduser=false;
try {
  //进行用户注册的记录添加操作,生成sql语句
String sSql=new String("insert into user(regtime,username,password,email,homepage,signs)");
sSql=sSql+ " values(SYSDAYE,""+Username+"",""+Password+"",""+Email+"",""+Homepage+"",""+Signs+"")";
  //一种调试的方法,可以打印出sql语句,以便于查看错误
System.out.println(sSql);
  //调用父类的executeUpdate方法,并根据成功以否来设置返回值
if(super.executeUpdate(sSql))boadduser=true;
}
catch(Exception ex) {

  //出错处理
System.err.println("adduser.addNewUser: " + ex.getMessage());
}finally{
  //无论是否出错,都要返回值
return boadduser;
}
}

  //checkUser()方法用来检查用户名是否重复

  //如果重复返回一个false
public boolean checkUser(){
boolean boadduser=false;
try {
  //构建sql查询语句
String sSql="select * from user where username=""+Use me+""";
  //调用父类的executeQuery方法
if((super.executeQuery(sSql)).next()){
  //查询出来的记录集为空
boadduser=false;
}else{
boadduser=true;
}
}
catch(Exception ex) {
  //出错处理
System.err.println("adduser.addNewUser: " + ex.getMessage());
}finally{
  //返回值
return boadduser;
}
}


  //属性的set/get方法,同请求的参数一致
/*
  其实下面的所有get/set方法都是重复性劳动,为了避免重复性的拷贝粘贴工作,我写了个软件Jsp Code Faster,只要输入一系列的字段名,所有的get/set方法都可以自动生成,大家可以在我的网站http://jspbbs.yeah.net上下载这个软件
*/
  //属性用户名Username的get/set方法
public String getUsername(){
return Username;}
public void setUsername(String newUsername){
  //用户名有可能是中文,需要进行转换
Username =db.toChinese(newUsername);}
  //属性密码Password的get/set方法
public String getPassword(){
return Password;}
public void setPassword(String newPassword){
Password = newPassword;}
  //属性Email的get/set方法
public String getEmail(){
return Email;}
public void setEmail(String newEmail){
Email = newEmail;}
  //属性主页Homepage的get/set方法
public String getHomepage(){
return Homepage;}
public void setHomepage(String newHomepage){
Homepage = newHomepage;}

  //属性主页Signs的get/set方法
public String getSigns(){
return Signs;}
public void setSigns(String newSigns){
  //签名有可能是中文,需要进行转换
Signs = db.toChinese(newSigns);}
}
  好了,到这里,Javabean程序基本上写完了,要注意的是中文处理一定要经过转换,还有不一定所有的属性都需要set/get方法,视情况而定,最后还需要编译为class文件,可以使用一些可视化的软件如Jbuilder或者VisualAge等等来编译。编译后会发现有两个文件db.class和adduser.class文件,都在lyf子目录下。这两个文件就可以让后面的jsp页面调用了。


Jsp + JavaBean循序渐进教程(四)上

发表人:lewang | 发表时间: 2006年五月13日, 13:29

JavaServer Pages+JavaBeans的数据库操作应用

  上面已经讲了一个简单的JavaBean应用的计数器例子,当然在实际程序过程中,涉及的更多的还是和数据库相关的操作,所以在这一节我们将重点阐述JavaServer Pages和JavaBeans如何对数据库进行操作。这里我们选取了一个比较有代表性比较实用的例子,那就是用户注册管理,因为这在网上使用比较的频繁,不管是注册Email、有奖调查、购买物品或者加入社区等等都会涉及到一个用户注册的问题;另外一方面,它又比较有代表性,涉及到了数据库的记录增加,记录显示等常见操作,所以我们就拿用户注册开刀了。

  程序采用Oracle Jdeveloper3.1开发,运行环境为Wiin2000+Tomcat3.1,数据库系统采用了Oracle8.16i。

  首先我们建立一个数据库demodb,其字段如下面所示

username VARCHAR2(20) 用户名
password VARCHAR2(20) 密码
email VARCHAR2(30) Email地址
homepage VARCHAR2(50) 主页
signs VARCHAR2(200) 签名
regtime DATE 注册时间

  接着我们建立几个JavaBeans和JavaServer Pages文件

db.java文件(封装数据库连接及一些底层操作)
adduser.java文件(进行用户数据的读取以及添加操作)
newuser.jsp文件(用户新增页面,用于输入用户注册信息)
donewuser.jsp文件(进行用户注册信息添加)
listuser.jsp文件(所有的注册用户信息列表)

  为了方便大家看代码,在很多地方都进行了详细的注释和讲解,至于JavaBean中涉及到Java语法结构的东西,请大家参考Java书籍。

  db.java文件

  说明:这个JavaBean封装数据库连接及一些底层操作,派生出的类可以直接调用这些方法,另外提供了一个toChinese方法,主要用来进行中文数据的处理。

  // Copyright (c) 2000 http://jspbbs.yeah.net
package lyf;
/**
* A Class class.
* $#@60;P$#@62;
* @author liuyufeng
*/

  //声明类库文件
import oracle.jdbc.driver.*;
import java.net.*;
import java.sql.*;
import java.lang.*;
import java.io.*;
import java.util.*;
public class db br>   //成员变量初始化
Connection conn = null; //数据库连接
ResultSet rs = null; //记录集
String Username=""; //用户名
String Password=""; //密码
String Email=""; //email
String Homepage=""; //主页
String Signs=""; //签名
  //db的构建器
public db() {
try {
  //注册数据库驱动程序为Oracle驱动
Class.forName(new oracle.jdbc.driver.OracleDriver());
}
catch(java.lang.ClassNotFoundException e) {
  //这样写是为了方便调试程序,出错打印mydb()就知道在什么地方出错了
System.err.println("mydb(): " + e.getMessage());
}
}

  //executeQuery方法用于进行记录的查询操作

  //入口参数为sql语句,返回ResultSet对象
public ResultSet executeQuery(String sql) {
rs = null;
try {
  //建立数据库连接,使用Oracle的一种thin连接方式,demo为主机名字,demodb为数据库,后面的两个demo为用户名和密码
conn = DriverManager.getConnection("jdbc:oracle:thin:@demo:1521:demodb","demo","demo");
Statement stmt = conn.createStatement();
  //执行数据库查询操作
rs = stmt.executeQuery(sql);
}
catch(SQLException ex) {
System.err.println("db.executeQuery: " + ex.getMessage());
}
return rs;
}

  //executeUpdate方法用于进行add或者update记录的操作

  //入口参数为sql语句,成功返回true,否则为false
public boolean executeUpdate(String sql) {
boolean bupdate=false;
rs = null;
try {
  //建立数据库连接,其它参数说明同上面的一样
conn = DriverManager.getConnection("jdbc:oracle:thin:@demo:1521:demodb","demo","demo");
Statement stmt = conn.createStatement();
int rowCount = stmt.executeUpdate(sql);
  //如果不成功,bupdate就会返回0
if(rowCount!=0)bupdate=true;
}
catch(SQLException ex) {
  //打印出错信息
System.err.println("db.executeUpdate: " + ex.getMessage());
}
return bupdate;
}

  //toChinese方法用于将一个字符串进行中文处理

  //否则将会是???这样的字符串
public static String toChinese(String strvalue) {
try{
if(strvalue==null)
{
return null;
}
else
{
strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");
return strvalue;
}
}catch(Exception e){
return null;
}
}

}


Jsp + JavaBean循序渐进教程(三)

发表人:lewang | 发表时间: 2006年五月13日, 13:28

<font color="#FF0000">Jsp与JavaBean结合的简单例子

  上面讲了这么多,到现在还没有看到具体应用,那好,现在我们看看具体的JavaServer Pages+JavaBean的例子吧,首先让我们看看一个简单的计数器程序。

  本例程共包含3个文件

  JavaBean--counter.java文件,JavaServer Page-- counter.jsp文件, counter1.jsp文件其中,counter.java主要用来进行计数器的计数操作,counter.jsp和counter1.jsp文件主要用来显示网页的计数。

  counter.java文件

package count;
/**
* Title: test
* Description: counter Bean
* @author LiuYufeng
* @version 1.0
*/
public class counter {
  //初始化JavaBean的成员变量
int count = 0;
  // Class构造器
public counter() {
}
  // 属性Count的Get方法
public int getCount() {
  //计数操作,每一次请求都进行计数器加一
count++;
return this.count;
}
  //属性Count的Set方法
public void setCount(int count) {
this.count = count;
}
}

  counter.jsp文件

<HTML>
<HEAD>
<TITLE>
counter
</TITLE>
</HEAD>
<BODY>
<H1>
JBuilder Generated JSP
</H1>
<!-初始化counter这个Bean,实例为bean0-->
<jsp:useBean id="bean0" scope="application" class="count.counter" />
<%

  //显示当前的属性count的值,也就是计数器的值,这里我们使用out.println方法,下面的counter1.jsp将使用另一种方法
out.println("The Counter is : " + bean0.getCount() + "<BR>");
%>
</BODY>
</HTML>

  counter1.jsp文件

<HTML>
<HEAD>
<TITLE>
counter
</TITLE>
</HEAD>
<BODY>
<H1>
JBuilder Generated JSP
</H1>
<!-初始化counter这个Bean,实例为bean0-->
<jsp:useBean id="bean0" scope="application" class="count.counter" />
<!-使用jsp:getProperty 标签得到count属性的值,也就是计数器的值-->
The Counter is :
<jsp:getProperty name="bean0" property="count" /><BR>
</BODY>
</HTML>

  从这个例子我们不难看出Jsp和JavaBean应用的一般操作方法,首先在Jsp页面中要声明并初始化JavaBean,这个JavaBean有一个唯一的id标志,还有一个生存范围scope(设置为application是为了实现多个用户共享一个计数器的功能,如果要实现单个用户的计数功能,可以修改scope为session),最后还要制定JavaBean的class来源count.counter:

  <jsp:useBean id="bean0" scope="application" class="count.counter" />

  接着我们就可以使用JavaBean提供的public方法或者直接使用<jsp:getProperty>标签来得到JavaBean中属性的值:

  out.println("The Counter is : " + bean0.getCount() + "<BR>");

或者

  <jsp:getProperty name="bean0" property="count" />

  OK,现在运行一下程序看看,然后多刷新几次,注意看计数器的变化。上面的程序在Jbuilder4.0下面调试通过。

  如果要直接在一些jsp环境(如Tomcat、IAS、Weblogic等)下调试,请注意各自的文档,正确的放置JavaBean文件。如在Tomcat环境中,本例子JavaBean编译后的文件就需要放在<Server Root>WEB-INFClASSES count counter.class。


Jsp + JavaBean循序渐进教程(二)

发表人:lewang | 发表时间: 2006年五月13日, 13:28

JavaBeans 和 JavaServer Pages的结合

  通过上面的学习,大家对JavaBean应该有了一个基本的了解,对于在JavaServer Pages中调用JavaBeans我们还需要了解一些特定的jsp的内容,让我们来看看吧。

  JavaServer Pages中的JavaBean相关标签

  在JavaServer Pages中调用JavaBean有三个标准的标签,那就是<jsp:useBean>, <jsp:setProperty>,以及<jsp:getProperty>。

  <jsp:useBean>标签

  <jsp:useBean>可以定义一个具有一定生存范围以及一个唯一id的JavaBean的实例,这样JavaServer Pages通过id来识别JavaBean,也可以通过id.method类似的语句来操作JavaBean。

  在执行过程中,<jsp:useBean>首先会尝试寻找已经存在的具有相同id和scope值的JavaBean实例,如果没有就会自动创建一个新的实例。

  其具体语法如下

<jsp:useBean id=“name”
scope=“page|request|session|application”
typeSpec>
body
</jsp:useBean>

  其中,typeSpec定义如下

typeSpec ::=class=“className”
| class=“className” type=“typeName”
| type=“typeName” class=“className”
| beanName=“beanName” type=“typeName”
| type=“typeName” beanName=“beanName”
| type=“typeName”

  下面的表格是<jsp:useBean>标签中相关属性的含义

  属性及定义

Id
  id属性是JavaBean对象的唯一标志,代表了一个JavaBean对象的实例。它具有特定的存在范围(page|request|session|application)。在JavaServer Pages中通过id来识别JavaBean。

Scope
  Scope属性代表了Javabean对象的生存时间,可以是page, request, session, 和 application中的一种。

Class
  代表了JavaBean对象的class名字,特别注意大小写要完全一致。

beanName
  BeanName属性代表了Bean的名字,通常通过java.beans.Beans class的 tantiate() 方法来初始化。.

Type
  Type属性指定了脚本变量定义的类型,默认为脚本变量定义和class中的属性一致,一般我们都采用默认值

  <jsp:setProperty>标签

  另一个标准的标签就是<jsp:setProperty>标签了,它主要用于设置bean的属性值。JavaServer Pages中调用的语法如下:

  <jsp:setProperty name=“beanName” last_syntax />

  其中,name属性代表了已经存在的并且具有一定生存范围(scope)的JavaBean实例。last_syntax代表的语法如下: property=“*” |
property=“propertyName” |
property=“propertyName” param=“parameterName” |
property=“propertyName” value=“propertyValue”

  下面是<jsp:setProperty>标签的基本属性以及含义

  属性及定义

Name
  Name代表通过<jsp:useBean> 标签定义的JavaBean对象实例。

Property
  这是个很重要的属性,代表了你想设置值的属性property名字。如果使用property=”*”,程序就会反复的查找当前的ServletRequest所有参数,并且匹配JavaBean中相同名字的属性property,并通过JavaBean中属性的set方法赋值value给这个属性。如果value属性为空,则不会修改Javabean中的属性值。

Param
  Param属性代表了页面请求的参数名字,<jsp:setProperty>标签不能同时使用param和value。

Value
  Value属性代表了赋给Bean的属性property的具体值。

  <jsp:getProperty>标签

  最后一个标签就是<jsp:getProperty>标签了.它可以得到JavaBean实例的属性值,并将他们转换为 java.lang.String, 最后放置在隐含的Out对象中. JavaBean的实例必须在<jsp:getProperty>前面定义.

  <jsp:getProperty>标签的语法如下:

  <jsp:getProperty name=“name” property=“propertyName” />

  下面是<jsp:getProperty>标签的基本属性以及含义

  属性及定义

Name
  Name属性代表了想要获得属性值的Bean的实例,Bean实例必须在前面用<jsp:useBean>标签定义.

Property
  Property属性代表了想要获得值的那个property的名字。


Jsp + JavaBean循序渐进教程(一)

发表人:lewang | 发表时间: 2006年五月13日, 13:27

Jsp作为一个很好的动态网站开发语言得到了越来越广泛的应用,在各类Jsp应用程序中,Jsp + Java Bean的组合成为了一种事实上最常见的Jsp程序的标准,就让我们来看看具体的Jsp是如何与Java Bean结合在一起的吧。

  本教程将阐述JavaBean的原理,接着将阐述JavaBean在JavaServer Page下的特定语法,然后演示一个使用Jsp+JavaBean的简单的计数器,最后将详细的讲解一个有数据库功能的JavaBean+jsp的用户注册程序。

Java Bean原理与应用

  1、什么是JavaBean?

  JavaBean是描述Java的软件组件模型,有点类似于Microsoft的COM组件概念。在Java模型中,通过JavaBean可以无限扩充Java程序的功能,通过JavaBean的组合可以快速的生成新的应用程序。对于程序员来说,最好的一点就是JavaBean可以实现代码的重复利用,另外对于程序的易维护性等等也有很重大的意义。

  JavaBean通过Java虚拟机(Java Virtual Machine)可以得到正确的执行,运行JavaBean最小的需求是JDK1.1或者以上的版本。

  JavaBean传统的应用在于可视化的领域,如AWT下的应用。自从Jsp诞生后,JavaBean更多的应用在了非可视化领域,在服务器端应用方面表现出来了越来越强的生命力。在这里我们主要讨论的是非可视化的JavaBean,可视化的JavaBean在市面上有很多Java书籍都有详细的阐述,在这里就不作为重点了。

  2、非可视化的JavaBean

  非可视化的JavaBean,顾名思义就是没有GUI界面的JavaBean。在Jsp程序中常用来封装事务逻辑、数据库操作等等,可以很好地实现业务逻辑和前台程序(如jsp文件)的分离,使得系统具有更好的健壮性和灵活性。

  一个简单的例子,比如说一个购物车程序,要实现购物车中添加一件商品这样的功能,就可以写一个购物车操作的JavaBean,建立一个public的AddItem成员方法,前台Jsp文件里面直接调用这个方法来实现。如果后来又考虑添加商品的时候需要判断库存是否有货物,没有货物不得购买,在这个时候我们就可以直接修改JavaBean的AddItem方法,加入处理语句来实现,这样就完全不用修改前台jsp程序了。

  当然,也可以把这些处理操作完全写在jsp程序中,不过这样的jsp页面可能就有成百上千行,光看代码就是一个头疼的事情,更不用说修改了。如果您使用过asp开发过程序的话,相信对这就深有体会? 涫凳褂肁SP+COM组件完全可以实现同jsp+javabean同样的架构,但不知道某种原因网上常见的都是全部写在asp页面中,所以使得维护修改等极为不方便,当然这是题外话了)。由此可见,通过JavaBean可以很好地实现逻辑的封装、程序的易于维护等等。

  如果您使用Jsp开发程序,一个很好的习惯就是多使用JavaBean。

  3、JavaBean的简单例子

  创建JavaBean并不是一件困难的事情,如果您写过Java程序的话那就很容易了,要注意一点的就是在非可视化JavaBean中,常用>get或者>set这样的成员方法来处理属性>(properties>)。>

  下面让我们来看一个简单的JavaBean

FirstJavaBean.java

import java.io.*;

public class FirstJavaBean {

private String FirstProperty = new String("");

public FirstJavaBean() {

}

public String getFirstProperty() {

return FirstProperty;

}

public void setFirstProperty(String value) {

FirstProperty = value;

}

public static void main(String[] args)

{

System.out.println("My First JavaBean!");

}

}

如果运行这个程序,就会出现下面的结果:

First JavaBean!

  这是一个很典型的JavaBean的代表,简单地解释一下,FirstProperty是其中的一个属性(Property),外部通过get/set方法可以对这个属性进行操作,如果您写过VB的class的话,对这个就再也熟悉不过了。Main方法是为了测试程序用的,写JavaBean可以先不必加入到Jsp程序中调用,而直接用main方法来进行调试,调试好以后就可以在Jsp程序中调用了。


提高Java代码可重用性的三个措施(3)

发表人:lewang | 发表时间: 2006年五月13日, 13:26

而Rectangular接口的定义是:

  public interface Rectangular {Rectangle getBounds();}

  现在,所有可以描述为矩形的类(即,实现了Rectangular接口的类)所创建的对象都可以作为提供给pRectangular.contains()的rect参数。通过放宽参数类型的限制,我们使方法具有更好的可重用性。

  不过,对于上面这个例子,Rectangular接口的getBounds方法返回Rectangle,你可能会怀疑这么做是否真正值得。换言之,如果我们知道传入过程的对象会在被调用时返回一个Rectangle,为什么不直接传入Rectangle取代接口类型呢?之所以不这么做,最重要的原因与集合有关。让我们假设有这样一个方法:

  static public boolean areAnyOverlapping(Collection rects) {...}

  该方法用于检查给定集合中的任意矩形对象是否重叠。在这个方法的内部,当我们用循环依次访问集合中的各个对象时,如果我们不能把对象cast成为Rectangular之类的接口类型,又如何能够访问对象的矩形区域呢?唯一的选择是把对象cast成为它特有的类形式(我们知道它有一个方法可以返回矩形),它意味着方法必须事先知道它所操作的对象类型,从而使得方法的重用只限于那几种对象类型。而这正是前面这个措施力图先行避免的问题!

  措施三:选择最简单的参数接口类型
  在实施第二个措施时,应该选用哪一种接口类型来取代给定的类形式?答案是哪一个接口完全满足过程对参数的需求,同时又具有最少的多余代码和数据。描述参数对象要求的接口越简单,其他类实现该接口的机会就越大——由此,其对象能够作为参数使用的类也越多。从下面这个例子可以很容易地看出这一点:

  static public boolean areOverlapping(Window window1, Window window2) {...}

  这个方法用于检查两个窗口(假定是矩形窗口)是否重叠。如果这个方法只要求从参数获得两个窗口的矩形坐标,此时相应地简化这两个参数是一种更好的选择:

  static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}

  上面的代码假定Window类型实现了Rectangular接口。经过改动之后,对于任何矩形对象我们都可以重用该方法的功能。

  有些时候可能会出现描述参数需求的接口拥有太多方法的情况。此时,我们应该在全局名称空间中定义一个新的公共接口供其他面临同一问题的代码重用。


  当我们需要象使用C语言中的函数指针一样使用参数时,创建唯一的接口描述参数需求是最好的选择。例如,假设有下面这个过程:

  static public void sort(List list, SortComparison comp) {...}

  该方法运用参数中提供的比较对象comp,通过比较给定列表list中的对象排序list列表。sort对comp对象的唯一要求是要调用一个方法进行比较。因此,SortComparison应该是只带有一个方法的接口:

  public interface SortComparison {
  boolean comesBefore(Object a, Object b);
  }

  SortComparison接口的唯一目的在于为sort提供一个它所需功能的钩子,因此SortComparison接口不能在其他地方重用。

  总而言之,本文三个措施适合于改造现有的、按照面向对象惯例编写的代码。这三个措施与面向对象编程技术结合就得到了一种可在以后编写代码时使用的新式代码编写技术,它能够简化方法的复杂性和依赖关系,同时提高方法的可重用能力和内部凝聚力。

  当然,这里的三个措施不能用于那些天生就不适合重用的代码。不适合重用的代码通常出现在应用的表现层。例如,创建程序用户界面的代码,以及联结到输入事件的控制代码,都属于那种在程序和程序之间千差万别的代码,这种代码几乎不可能重用。


提高Java代码可重用性的三个措施(2)

发表人:lewang | 发表时间: 2006年五月13日, 13:26


  static public boolean
  containsPoint(Polygon polygon, Point p) {...}
  }

  从类的名字pPolygon可以看出,该类所封装的过程主要与Polygon类型的对象有关。名字前面的p表示该类的唯一目的是组织公用静态过程。在Java中,类的名字以小写字母开头是一种非标准的做法,但象pPloygon这样的类事实上并不提供普通Java类的功能。也就是说,它并不代表着一类对象,它只是Java语言组织代码的一种机制。

  在上面这个例子中,改动代码的最终效果是使得应用Polygon功能的客户代码不必再从Polygon继承。Polygon类的功能现在已经由pPolygon类以过程为单位提供。客户代码只使用自己需要的代码,无需关心Polygon类中自己不需要的功能。但它并不意味着在这种新式过程化编程中类的作用有所削弱。恰恰相反,在组织和封装对象数据成员的过程中,类起到了不可或缺的作用,而且正如本文接下来所介绍的,类通过多重接口实现多态性的能力本身也带来了卓越的代码重用支持。然而,由于用实例方法封装代码功能并不是首选的代码重用手段,所以通过类继承达到代码重用和多态性支持也不是最理想的。

  措施二:把参数类型改成接口
  正如Allen Holub在《Build User Interfaces for Object-Oriented Systems》中所指出的,在面向对象编程中,代码重用真正的要点在于通过接口参数类型利用多态性,而不是通过类继承:

  “……我们通过对接口而不是对类编程达到代码重用的目的。如果某个方法的所有参数都是对一些已知接口的引用,那么这个方法就能够操作这样一些对象:当我们编写方法的代码时,这些对象的类甚至还不存在。从技术上说,可重用的是方法,而不是传递给方法的对象。”

  在“措施一”得到的结果上应用Holub的看法,当某块代码能够编写为独立的全局过程时,只要把它所有类形式的参数改为接口形式,我们就可以进一步提高它的可重用能力。经过这个改动之后,过程的参数可以是实现了该接口的所有类的对象,而不仅仅是原来的类所创建的对象。由此,过程将能够对可能存在的大量的对象类型进行操作。

  例如,假设有这样一个全局静态方法:

  static public boolean contains(Rectangle rect, int x, int y) {...}

  这个方法用于检查指定的点是否包含在矩形里面。在这个例子中,rect参数的类型可以从Rectangle类改变为接口类型,如下所示:

  static public boolean contains(Rectangular rect, int x, int y) {...}


提高Java代码可重用性的三个措施(1)

发表人:lewang | 发表时间: 2006年五月13日, 13:25

本文介绍了三种修改现有代码提高其可重用性的方法,它们分别是:改写类的实例方法,把参数类型改成接口,选择最简单的参数接口类型。

  措施一:改写类的实例方法
  通过类继承实现代码重用不是精确的代码重用技术,因此它并不是最理想的代码重用机制。换句话说,如果不继承整个类的所有方法和数据成员,我们无法重用该类里面的单个方法。继承总是带来一些多余的方法和数据成员,它们总是使得重用类里面某个方法的代码复杂化。另外,派生类对父类的依赖关系也使得代码进一步复杂化:对父类的改动可能影响子类;修改父类或者子类中的任意一个类时,我们很难记得哪一个方法被子类覆盖、哪一个方法没有被子类覆盖;最后,子类中的覆盖方法是否要调用父类中的对应方法有时并不显而易见。

  任何方法,只要它执行的是某个单一概念的任务,就其本身而言,它就应该是首选的可重用代码。为了重用这种代码,我们必须回归到面向过程的编程模式,把类的实例方法移出成为全局性的过程。为了提高这种过程的可重用性,过程代码应该象静态工具方法一样编写:它只能使用自己的输入参数,只能调用其他全局性的过程,不能使用任何非局部的变量。这种对外部依赖关系的限制简化了过程的应用,使得过程能够方便地用于任何地方。当然,由于这种组织方式总是使得代码具有更清晰的结构,即使是不考虑重用性的代码也同样能够从中获益。

  在Java中,方法不能脱离类而单独存在。为此,我们可以把相关的过程组织成为独立的类,并把这些过程定义为公用静态方法。

  例如,对于下面这个类:

  class Polygon {
  .
  .
  public int getPerimeter() {...}
  public boolean isConvex() {...}
  public boolean containsPoint(Point p) {...}
  .
  .
  }

  我们可以把它改写成:

  class Polygon {
  .
  .
  public int getPerimeter() {return pPolygon.computePerimeter(this);}
  public boolean isConvex() {return pPolygon.isConvex(this);}
  public boolean containsPoint(Point p) {return pPolygon.containsPoint(this, p);}
  .
  }

  其中,pPolygon是:

  class pPolygon {
  static public int computePerimeter(Polygon polygon) {...}
  static public boolean isConvex(Polygon polygon) {...}


soap规范(5)

发表人:lewang | 发表时间: 2006年五月13日, 13:23


下例是一个schema片断,电话号码数组嵌入到一个类型为"Person"的结构中,并且通过accessor "phone-numbers"访问它:

<simpleType name="phoneNumber" base="string"/>

<element name="ArrayOfPhoneNumbers">
<complexType base="SOAP-ENC:Array">
<element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded"/>
</complexType>
<anyAttribute/>
</element>

<element name="Person">
<complexType>
<element name="name" type="string"/>
<element name="phoneNumbers" type="tns:ArrayOfPhoneNumbers"/>
</complexType>
</element>

<xyz:Person>
<name>John Hancock</name>
<phoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">
<phoneNumber>206-555-1212</phoneNumber>
<phoneNumber>1-888-123-4567</phoneNumber>
</phoneNumbers>
</xyz:Person>

下面的例子中,数组值为single-reference,被编码为嵌入元素,包含它的元素名即为入口名:

<xyz:PurchaseOrder>
<CustomerName>Henry Ford</CustomerName>
<ShipTo>
<Street>5th Ave</Street>
<City>New York</City>
<State>NY</State>
<Zip>10010</Zip>
</ShipTo>
<PurchaseLineItems SOAP-ENC:arrayType="Order[2]">
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>
<Order>
<Product>Peach</Product>
<Price>1.48</Price>
</Order>
</PurchaseLineItems>
</xyz:PurchaseOrder>

5.4.2.1 部分储值(partially transmitted)数组
SOAP提供了对部分储值(partially transmitted)数组的支持,如某些上下文中的可变数组。一个partially transmitted 数组由一个"SOAP-ENC:offset"属性(从第一个transmitted的元素开始的偏移量,基于0)指示。如果省略,偏移量取0。

下面的例子中数组的大小为5,但只有从0起,第三和第四个元素被储值。

<SOAP-ENC:Array ;SOAP-ENC:arrayType="xsd:string[5]" ;SOAP-ENC:offset="[2]">
<item>The third element</item>
<item>The fourth element</item>
</SOAP-ENC:Array>

5.4.2.2 稀疏数组Sparse Arrays
SOAP提供了对稀疏数组的支持。每个表示成员值的元素包含一个"SOAP-ENC:position"属性,用来指示它在数组中的位置。下例是两维字符串稀疏数组的例子,数组大小是4,但只用到第2个。

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">
<SOAP-ENC:Array href="#array-1" SOAP-ENC:position="[2]"/>
</SOAP-ENC:Array>
<SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[10,10]">
<item SOAP-ENC:position="[2,2]">Third row, third col</item>
<item SOAP-ENC:position="[7,2]">Eighth row, third col</item>
</SOAP-ENC:Array>

如果对array-1的引用仅发生在数组内部,上例也可以编码如下:

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">
<SOAP-ENC:Array SOAP-ENC:position="[2]" SOAP-ENC:arrayType="xsd:string[10,10]>
<item SOAP-ENC:position="[2,2]">Third row, third col</item>
<item SOAP-ENC:position="[7,2]">Eighth row, third col</item>
</SOAP-ENC:Array>
</SOAP-ENC:Array>

5.4.3 一般复合类型
在这里提到的编码规则不仅仅限于accessor名已知的情况,如果accessor名是运行环境下实时获得的,编码规则同样适用,也就是说accessor编码成一个元素名与accessor名匹配的元素,同时accessor可能包含或者引用该元素的值。如果accessor包含类型不能事先确定的值,它必须包含一个合适的属性xsi:type 。
类似地,上述引用的规则已经足够用于复合类型的序列化,这些复合类型可能包含用名区分的accessors(结构)和用名及序数位置区分的accessors。(可能包含重复的accessor)实际上这并不要求任何schema模式包含这些类型,但更为准确的说法是:一个类型模型(type-model)schema如果有这些类型,就可以构造一个符合XML句法规则的schema和XML文档实例。

 

<xyz:PurchaseOrder>
<CustomerName>Henry Ford</CustomerName>
<ShipTo>
<Street>5th Ave</Street>
<City>New York</City>
<State>NY</State>
<Zip>10010</Zip>
</ShipTo>
<PurchaseLineItems>
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>
<Order>
<Product>Peach</Product>
<Price>1.48</Price>
</Order>
</PurchaseLineItems>
</xyz:PurchaseOrder>

类似地,将一个结构上类似数组但实际上不是一个 SOAP-ENC:Array类型或者 SOAP-ENC:Array子类型的复合值序列化同样是允许的,例如:

<PurchaseLineItems>
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>
<Order>
<Product>Peach</Product>
<Price>1.48</Price>
</Order>
</PurchaseLineItems>

5.5 缺省值
省略accessor元素意味着或者有一个缺省值或者值不知道。具体细节依靠这个accessor,方法和上下文。例如,对于多态accessor,省略accessor一般意味着一个Null值。同样,省略布尔accessor一般意味着False值或者值不知道,省略数字accessor一般意味着值为零或者值不知道。

5.6 SOAP root属性
SOAP root 属性可用于标记一个序列化root,从而一个对象可以反序列化(deserialized),而实际上该root并不是真正的对象root。这个属性有两个可选值"1" or "0"。对象真正的roots属性值为“1” ,序列化root但不是真正的root属性值也为“1”,元素如果要显式地指定不能为序列化root,只需将该属性设置为“0” SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。(译者注:SOAP root属性为0的元素不是一个独立的实体,外部的应用不能访问到该元素,但该元素可以被SOAP文档本身的其它元素访问到)

SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。这个属性没有缺省值。


soap规范(4)

发表人:lewang | 发表时间: 2006年五月13日, 13:23

5.4.2 数组
SOAP数组定义为具有"SOAP-ENC:Array"类型或一个从"SOAP-ENC:Array"衍生的类型(参见规则8)。数组表示为元素值,对元素的名没有特别的约束(正如元素值并不约束它们所属的元素)。

数组可以包含任意类型的元素,包括嵌套数组。可以创建新的类型(受SOAP-ENC:Array类型限制)来表示数组,如整数数组或某些用户定义的枚举。

数组值表示为组成这个数组的项的元素的规则序列。在数组值中,元素名对于区分accesor并不重要。元素可以有任意的名。实际上,元素常常用它们在schema中暗示或确定的数组类型来命名元素。并且一般情况下对于复合值来说,如果数组中数组项的值是single-reference值,则这个数组项包含它的值,否则,该数组项通过"href"属性引用这个值。

下面的例子是一个整型数组的schema片断:

<element name="myFavoriteNumbers"
type="SOAP-ENC:Array"/>

<myFavoriteNumbers
SOAP-ENC:arrayType="xsd:int[2]">
<number>3</number>
<number>4</number>
</myFavoriteNumbers>

在这个例子中,数组"myFavoriteNumbers"包括几个成员,每个成员是一个类型为SOAP-ENC:int的值。注意SOAP-ENC:Array允许不受限制的元素名,它们不传达任何类型信息,所以在使用时,或者它们有xsi:type属性,或者它们所属的元素有SOAP-ENC:arrayType属性。自然,由SOAP-ENC:Array衍生的类型可以声明局部元素,但这种情况下要包括类型信息。

上面已经提到,SOAP-ENC schema包含了元素的声明,元素名与"XML Schema Part 2: Datatypes"规范[11]中的简单类型一致。其中包括了对"Array"的声明。于是,我们可以这样写:

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:int[2]">
<SOAP-ENC:int>3</SOAP-ENC:int>
<SOAP-ENC:int>4</SOAP-ENC:int>
</SOAP-ENC:Array>

数组可以包含特定arrayType的任意子类型的实例。即,数组成员可以是arryType属性值指定的类型的任意子类型,这个类型对于arrayType属性中指定的类型来说是可替换的(根据schema中的替换规则)。例如,一个整型数组可以包含从整型衍生的任意类型(如"int"或任意用户定义的从整型衍生的类型)。同样,一个"address"数组可能包含一个address的受限类型或扩展类型如"internationalAddress"。因为提供的SOAP-ENC:Array类型允许任意类型的成员,所以可以包含任意类型的混合除非使用arrayType属性加以特别的限制。

在实例中,可以使用xsi:type指定成员元素的类型,或通过schema中成员元素的声明来指定。下面是两个例子。

<SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">
<thing xsi:type="xsd:int">12345</thing>
<thing xsi:type="xsd:decimal">6.789</thing>
<thing xsi:type="xsd:string">
Of Mans First Disobedience, and the Fruit
Of that Forbidden Tree, whose mortal tast
Brought Death into the World, and all our woe,
</thing>
<thing xsi:type="xsd:uriReference">
http://www.dartmouth.edu/~milton/reading_room/
</thing>
</SOAP-ENC:Array>

<SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">
<SOAP-ENC:int>12345</SOAP-ENC:int>
<SOAP-ENC:decimal>6.789</SOAP-ENC:decimal>
<xsd:string>
Of Mans First Disobedience, and the Fruit
Of that Forbidden Tree, whose mortal tast
Brought Death into the World, and all our woe,
</xsd:string>
<SOAP-ENC:uriReference>
http://www.dartmouth.edu/~milton/reading_room/
</SOAP-ENC:uriReference >
</SOAP-ENC:Array>

数组值可以是结构或其它复合值。例如"xyz:Order"结构数组:

<SOAP-ENC:Array SOAP-ENC:arrayType="xyz:Order[2]">
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>
<Order>
<Product>Peach</Product>
<Price>1.48</Price>
</Order>
</SOAP-ENC:Array>

数组成员值也可以是数组。下例是两个字符串数组组成的数组:

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[][2]">
<item href="#array-1"/>
<item href="#array-2"/>
</SOAP-ENC:Array>
<SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[2]">
<item>r1c1</item>
<item>r1c2</item>
<item>r1c3</item>
</SOAP-ENC:Array>
<SOAP-ENC:Array id="array-2" SOAP-ENC:arrayType="xsd:string[2]">
<item>r2c1</item>
<item>r2c2</item>
</SOAP-ENC:Array>

包含数组的元素无需命名为"SOAP-ENC:Array"。它可以有任意的名,只要元素的类型是SOAP-ENC:Array或由之衍生的类型。例如,下面是一个schema片断和与之一致的数组实例。

<simpleType name="phoneNumber" base="string"/>

<element name="ArrayOfPhoneNumbers">
<complexType base="SOAP-ENC:Array">
<element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded"/>
</complexType>
<anyAttribute/>
</element>

<xyz:ArrayOfPhoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">
<phoneNumber>206-555-1212</phoneNumber>
<phoneNumber>1-888-123-4567</phoneNumber>
</xyz:ArrayOfPhoneNumbers>

数组可能是多维的。在这种情况下,在arrayType属性的asize部分将不止有一个值:

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[2,3]">
<item>r1c1</item>
<item>r1c2</item>
<item>r1c3</item>
<item>r2c1</item>
<item>r2c2</item>
<item>r2c3</item>
</SOAP-ENC:Array>

虽然上面的例子把数组编码为独立的元素,但元素值也可以是嵌入形式,而且若元素值是single reference时,必须编码为嵌入形式。


soap规范(完)

发表人:lewang | 发表时间: 2006年五月13日, 13:23

6. 在HTTP中使用SOAP
这一节讲述了如何在HTTP中使用SOAP。把SOAP绑定到HTTP,无论使用或不用HTTP扩展框架,都有很大的好处:在利用SOAP的形式化和灵活性的同时,使用HTTP种种丰富的特性。在HTTP中携带SOAP消息,并不意味着SOAP改写了HTTP已有的语义,而是将构建在HTTP之上SOAP语义自然地对应到HTTP语义。

SOAP自然地遵循HTTP的请求/应答消息模型使得SOAP的请求和应答参数可以包含在HTTP请求和应答中。注意,SOAP的中间节点与HTTP的中间节点并不等同,即,不要期望一个根据HTTP连接头中的域寻址到的HTTP中间节点能够检查或处理HTTP请求中的SOAP消息。

在HTTP消息中包含SOAP实体时,按照RFC2376[3] HTTP应用程序必须使用媒体类型 "text/xml"。

6.1 SOAP HTTP请求
虽然SOAP可能与各种HTTP请求方式相结合,但是绑定仅定义了在HTTP POST请求中包含SOAP消息。(第7节中描述了如何在RPC中使用SOAP,第6.3节描述了如何使用HTTP扩展框架)

6.1.1 HTTP头中SOAPAction域
一个HTTP请求头中的SOAPAction域用来指出这是一个SOAP HTTP请求,它的值是所要的URI。在格式、URI的特性和可解析性上没有任何限制。当HTTP客户发出SOAP HTTP请求时必须使用在HTTP头中使用这个域。

soapaction = "SOAPAction" ":" [ <"> URI-reference <"> ]
URI-reference = <as defined in RFC 2396 [4]>

HTTP头中SOAPAction域使服务器(如防火墙)能正确的过滤HTTP中SOAP请求消息。如果这个域的值是空字符串(""),表示SOAP消息的目标就是HTTP请求的URI。这个域没有值表示没有SOAP消息的目标的信息。

例子:

SOAPAction: "http://electrocommerce.org/abc#MyMessage"
SOAPAction: "myapp.sdl"
SOAPAction: ""
SOAPAction:

6.2 SOAP HTTP应答
SOAP HTTP遵循HTTP 中表示通信状态信息的HTTP状态码的语义。例如,2xx状态码表示这个包含了SOAP组件的客户请求已经被成功的收到,理解和接受。

在处理请求时如果发生错误,SOAP HTTP服务器必须发出应答HTTP 500 "Internal Server Error",并在这个应答中包含一个SOAP Fault元素(见4.4节)表示这个SOAP处理错误。

6.3 HTTP扩展框架
一个SOAP消息可以与HTTP扩展框架 [6]一起使用以区分是否有SOAP HTTP请求和它的目标。

是使用扩展框架或是普通的HTTP关系到通信各方的策略和能力。通过使用一个必需的扩展声明和"M-"HTTP方法名前缀,客户可以强制使用HTTP扩展框架。服务器可以使用HTTP状态码510 "Not Extended"强制使用HTTP扩展框架。也就是说,使用一个额外的来回,任何一方都可以发现另一方的策略并依照执行。

用来表示SOAP使用了扩展框架的扩展标志符是:

http://schemas.xmlsoap.org/soap/envelope/
6.4 SOAP HTTP举例
例3 使用POST的SOAP HTTP

POST /StockQuote HTTP/1.1
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "http://electrocommerce.org/abc#MyMessage"

<SOAP-ENV:Envelope...

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope...

例4 使用扩展框架的SOAP HTTP

M-POST /StockQuote HTTP/1.1
Man: "http://schemas.xmlsoap.org/soap/envelope/"; ns=NNNN
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
NNNN-SOAPAction: "http://electrocommerce.org/abc#MyMessage"

<SOAP-ENV:Envelope...

HTTP/1.1 200 OK
Ext:
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope...

7. 在RPC中使用SOAP
设计SOAP的目的之一就是利用XML的扩展性和灵活性来封装和交换RPC调用。这一节定义了远程过程调用和应答的统一表示形式。

虽然可以预计到这种表示形式最可能被用于与第5节中定义的编码方式相结合,但也可能有其它的表示形式。SOAP的encodingStyle属性(见4.3.2节)可以用来表明方法调用和应答都使用这一节所指定的表示方式。

在RPC中使用SOAP和SOAP协议绑定(见第6节)是紧密相关的。在使用HTTP作为绑定协议时,一个RPC调用自然地映射到一个HTTP请求,RPC应答同样映射到HTTP应答。但是,在RPC中使用SOAP并不限于绑定HTTP协议。

要进行方法调用,以下的信息是必需的:

目标对象的URI
方法名
方法signature(可选)
方法的参数
头数据(可选)
SOAP依靠协议绑定提供传送URI的机制。例如,对HTTP来说,请求的URI指出了调用的来源。除了必须是一个合法的URI之外,SOAP对一个地址的格式没有任何限制。(更多URI的信息参见 [4])

7.1 RPC和SOAP体
RPC方法调用和应答都包含在SOAP Body元素中(见4.3节),它们使用如下的表示形式:

一个方法调用用一个结构表示
一个方法调用被看作一个单个的结构,每个[in]和[in/out]参数有一个accessor。结构的名和类型与方法相同。
每个[in]和[in/out]参数都被看作一个accessor,这个accessor的名和类型与参数的名和类型相对应。它们的出现顺序和方法中定义的参数顺序相同。
一个方法应答用一个结构表示。
一个方法应答被看作一个单个的结构,返回值和每个[in]和[in/out]参数有一个accessor。第一个accessor是返回值,之后是参数accessor,参数accessor的出现顺序和方法中定义的参数顺序相同。
每个参数accessor的名称和类型与参数的名称和类型相对应。返回值accessor的名称并不重要。同样,结构的名称也不重要,不过,通常在方法名称的后面加上字符串"Response"作为结构的名称。
方法错误使用SOAP Fault元素(见4.4节)表示。如果绑定的协议有额外的规则表示错误,则这些规则也必须要遵从。
正如上面所述,方法调用和应答结构可以按照第5节中规则编码,或者用encodingStyle属性(见4.1.1节)指定编码方式。

应用程序可以处理缺少参数的请求,但是可能返回一个错误。

因为返回结果表示调用成功,错误表示调用失败,所以,在方法应答中同时包含返回结果和错误是错误的。

7.2 RPC和SOAP头
在RPC编码中,可能会有与方法请求有关但不是正规的方法signature的附加信息。如果这样,它必须作为SOAP头元素的子元素。

使用这种头元素的一个例子是在消息中传递事务ID。由于事务ID不是方法signature的一部分,通常由底层的组件而不是应用程序代码控制,所以没有一种直接的方法在调用中传递这个必要的信息。通过在头中添加一个给定名字的条目,接收方的事务管理器就可以析取这个事务ID,而且不影响远程过程调用的代码。

8. 安全性考虑
这篇文档中没有涉及完整性和保密性,这些问题将在以后的版本中描述。

9. 参考文献
[1] S. Bradner, "The Internet Standards Process -- Revision 3", RFC2026, Harvard University, October 1996

[2] S. Bradner, "Key words for use in RFCs to Indicate Requirement Levels", RFC 2119, Harvard University, March 1997

[3] E. Whitehead, M. Murata, "XML Media Types", RFC2376, UC Irvine, Fuji Xerox Info. Systems, July 1998

[4] T. Berners-Lee, R. Fielding, L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, MIT/LCS, U.C. Irvine, Xerox Corporation, August 1998.

[5] R. Fielding, J. Gettys, J. C. Mogul, H. Frystyk, T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, U.C. Irvine, DEC W3C/MIT, DEC, W3C/MIT, W3C/MIT, January 1997

[6] H. Nielsen, P. Leach, S. Lawrence, "An HTTP Extension Framework", RFC 2774, Microsoft, Microsoft, Agranat Systems

[7] W3C Recommendation "The XML Specification"

[8] W3C Recommendation "Namespaces in XML"

[9] W3C Working Draft "XML Linking Language". This is work in progress.

[10] W3C Working Draft "XML Schema Part 1: Structures". This is work in progress.

[11] W3C Working Draft "XML Schema Part 2: Datatypes". This is work in progress.

[12] Transfer Syntax NDR, in "DCE 1.1: Remote Procedure Call"

[13] N. Freed, N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC2045, Innosoft, First Virtual, November 1996

A. SOAP封装举例
A.1 请求编码举例
例5 类似于例1,但有一个必要的头

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Header>
<t:Transaction
xmlns:t="some-URI"
SOAP-ENV:mustUnderstand="1">
5
</t:Transaction>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m="Some-URI">
<symbol>DEF</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

例6 类似于例1,但有多个请求参数

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Body>
<m:GetLastTradePriceDetailed
xmlns:m="Some-URI">
<Symbol>DEF</Symbol>
<Company>DEF Corp</Company>
<Price>34.1</Price>
</m:GetLastTradePriceDetailed>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

A.2 应答编码举例
例7 与例2类似,但有必要的头部

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Header>
<t:Transaction
xmlns:t="some-URI"
xsi:type="xsd:int" mustUnderstand="1">
5
</t:Transaction>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse
xmlns:m="Some-URI">
<Price>34.5</Price>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

例8 与例2类似,但有一个结构

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse
xmlns:m="Some-URI">
<PriceAndVolume>
<LastTradePrice>
34.5
</LastTradePrice>
<DayVolume>
10000
</DayVolume>
</PriceAndVolume>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

例9 与例2类似,但处理必要的头出错

HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:MustUnderstand</faultcode>
<faultstring>SOAP Must Understand Error</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

例10 与例2类似,但处理Body出错

HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Server Error</faultstring>
<detail>
<e:myfaultdetails xmlns:e="Some-URI">
<message>
My application didn't work
</message>
<errorcode>
1001
</errorcode>
</e:myfaultdetails>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


soap规范(3)

发表人:lewang | 发表时间: 2006年五月13日, 13:22


以下是一个同时具有简单和复杂成员类型的例子。它显示两层引用。注意"Author"accssor元素的"href"属性是对相应具有"id"属性的值的引用。"Address"与之类似。

<e:Book>
<title>My Life and Work</title>
<author href="#Person-1"/>
</e:Book>
<e:Person id="Person-1">
<name>Henry Ford</name>
<address href="#Address-2"/>
</e:Person>
<e:Address id="Address-2">
<email>mailto:henryford@hotmail.com</email>
<web>http://www.henryford.com</web>
</e:Address>

当"Person"的值和"Address"的值是multi-reference时,上面的形式是正确的。如果它们是single-reference,就必须用嵌入的形式,如下所示:

<e:Book>
<title>My Life and Work</title>
<author>
<name>Henry Ford</name>
<address>
<email>mailto:henryford@hotmail.com</email>
<web>http://www.henryford.com</web>
</address>
</author>
</e:Book>
如果添加一个限制,任意两个人都不会有相同的地址,并且地址可以是街道或Email地址,一本书可以有两个作者,编码如下:

<e:Book>
<title>My Life and Work</title>
<firstauthor href="#Person-1"/>
<secondauthor href="#Person-2"/>
</e:Book>
<e:Person id="Person-1">
<name>Henry Ford</name>
<address xsi:type="m:Electronic-address">
<email>mailto:henryford@hotmail.com</email>
<web>http://www.henryford.com</web>
</address>
</e:Person>
<e:Person id="Person-2">
<name>Samuel Crowther</name>
<address xsi:type="n:Street-address">
<street>Martin Luther King Rd</street>
<city>Raleigh</city>
<state>North Carolina</state>
</address>
</e:Person>

序列化可以包含对不在同一个资源的值的引用:

<e:Book>
<title>Paradise Lost</title>
<firstauthor href="http://www.dartmouth.edu/~milton/"/>
</e:Book>

以下是描述上面结构的schema片断:

<element name="Book" type="tns:Book"/>
<complexType name="Book">
<!-- Either the following group must occur or else the
href attribute must appear, but not both. -->
<sequence minOccurs="0" maxOccurs="1">
<element name="title" type="xsd:string"/>
<element name="firstauthor" type="tns:Person"/>
<element name="secondauthor" type="tns:Person"/>
</sequence>
<attribute name="href" type="uriReference"/>
<attribute name="id" type="ID"/>
<anyAttribute namespace="##other"/>
</complexType>

<element name="Person" base="tns:Person"/>
<complexType name="Person">
<!-- Either the following group must occur or else the
href attribute must appear, but not both. -->
<sequence minOccurs="0" maxOccurs="1">
<element name="name" type="xsd:string"/>
<element name="address" type="tns:Address"/>
</sequence>
<attribute name="href" type="uriReference"/>
<attribute name="id" type="ID"/>
<anyAttribute namespace="##other"/>
</complexType>

<element name="Address" base="tns:Address"/>
<complexType name="Address">
<!-- Either the following group must occur or else the
href attribute must appear, but not both. -->
<sequence minOccurs="0" maxOccurs="1">
<element name="street" type="xsd:string"/>
<element name="city" type="xsd:string"/>
<element name="state" type="xsd:string"/>
</sequence>
<attribute name="href" type="uriReference"/>
<attribute name="id" type="ID"/>
<anyAttribute namespace="##other"/>
</complexType>


soap规范(1)

发表人:lewang | 发表时间: 2006年五月13日, 13:21

SOAP:简单对象访问协议
(2001-1-1)
摘要
SOAP是用在分散或分布的环境中交换信息的简单的协议,它是一个基于XML的协议,包括三个部分:封装定义了一个描述消息中包含什么内容以及如何处理它们的框架,编码规则用于表示应用程序定义的数据类型的实例,另外还有一个表示远程过程调用和应答的协定。SOAP被设计为可以与各种其它协议结合使用;但这篇文章仅描述如何将SOAP和HTTP及HTTP扩展框架相结合。

目录
1. 简介
1.1 设计目标
1.2 符号协定
1.3 SOAP消息举例
2. SOAP消息交换模型
3. 与XML的关系
4. SOAP封装
4.1.1 SOAP encodingStyle属性
4.1.2 封装版本模型
4.2 SOAP头
4.2.1 使用SOAP头属性
4.2.2 SOAP actor属性
4.2.3 SOAP mustUnderstand属性
4.3 SOAP体
4.3.1 SOAP头和体的关系
4.4 SOAP 错误
4.4.1 SOAP错误代码
5. SOAP编码
5.1 XML编码类型规则
5.2 简单类型
5.2.1 字符串
5.2.2 枚举
5.2.3 字符数组
5.3 多态 Accessor
5.4 复合类型
5.4.1 复合值和对值的引用
5.4.2 数组
5.4.2.1 PartiallyTransmitted Arrays
5.4.2.2 稀疏数组
5.4.3 一般复合类型
5.5 缺省值
5.6 SOAP root属性
6. 在HTTP中使用SOAP
6.1 SOAP HTTP请求
6.1.1 HTTP头中的SOAPAction域
6.2 SOAP HTTP应答
6.3 HTTP扩展框架
6.4 SOAP HTTP举例
7. 用SOAP表示RPC
7.1 RPC和SOAP体
7.2 RPC和SOAP头
8. 安全考虑
9. 参考文献
A. SOAP封装举例
A.1 请求编码举例
A.2 应答编码举例
1. 简介
SOAP以XML形式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型化信息的机制。SOAP本身并没有定义任何应用程序语义,如编程模型或特定语义的实现;实际上它通过提供一个有标准组件的包模型和在模块中编码数据的机制,定义了一个简单的表示应用程序语义的机制。这使SOAP能够被用于从消息传递到RPC的各种系统。

SOAP包括三个部分

SOAP封装(见第4节)结构定义了一个整体框架用来表示消息中包含什么内容,谁来处理这些内容以及这些内容是可选的或是必需的。
SOAP编码规则(见第5节)定义了用以交换应用程序定义的数据类型的实例的一系列机制。
SOAP RPC表示(见第7节)定义了一个用来表示远程过程调用和应答的协定。
虽然这三个部分都作为SOAP的一部分一起描述,但它们在功能上是相交的。特别的,封装和编码规则是在不同的名域中定义的,这种模块性的定义方法增加了简单性。

在SOAP封装,SOAP编码规则和SOAP RPC协定之外,这个规范还定义了两个协议的绑定,描述了在有或没有HTTP扩展框架[6]的情况下,SOAP消息如何包含在HTTP消息[5]中被传送。

1.1 设计目标
SOAP的主要设计目标是简单性和可扩展性,这意味着传统的消息系统和分布对象系统的某些性质不是SOAP规范的一部分。这些性质包括:

分布式碎片收集
成批传送消息
对象引用(要求分布式碎片收集)
激活机制(要求对象引用)
1.2 符号约定
这篇文章中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和"OPTIONAL"的解释在RFC-2119 [2]中。

这篇文章中用到的名域前缀 "SOAP-ENV" 和 "SOAP-ENC"分别与"http://schemas.xmlsoap.org/soap/envelope/" 和"http://schemas.xmlsoap.org/soap/encoding/"关联。

整篇文档中,名域前缀“xsi”被假定为与URI "http://www.w3.org/1999/XMLSchema-instance“(在XML Schema规范[11]定义)相连。类似的,名域前缀”xsd“被假定为与URI "http://www.w3.org/1999/XMLSchema"(在 [10]中定义)相连。名域前缀”tns“用来表示任意名域。所有其它的名域前缀都只是例子。

名域URI的基本形式”some-URI“表示某些依赖于应用程序或上下文的URI[4]。

这个规范用扩展BNF(在RFC-2616[5] 描述)描述某些结构。

1.3 SOAP消息举例
在这个例子中,GetLastTradePrice SOAP 请求被发往 StockQuote服务。这个请求携带一个字符串参数和ticker符号,在SOAP应答中返回一个浮点数。XML名域用来区分SOAP标志符和应用程序特定的标志符。这个例子说明了在第6节中定义的HTTP绑定。如果SOAP中管理XML负载的规则完全独立于HTTP是没有意义的,因为事实上该负载是由HTTP携带的。

在Appendix A中有更多的例子。

例1 在HTTP请求中嵌入SOAP消息

POST /StockQuote HTTP/1.1
Host:
www.stockquoteserver.com
Content-Type: text/xml;
charset="utf-8"
Content-Length: nnnn
SOAPAction:
"Some-URI"

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m="Some-URI">
<symbol>DIS</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

下面是一条应答消息,包括HTTP消息,SOAP消息是其具体内容:

例2 在HTTP应答中嵌入SOAP消息

HTTP/1.1 200 OK
Content-Type: text/xml;
charset="utf-8"
Content-Length:
nnnn

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse xmlns:m="Some-URI">
<Price>34.5</Price>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

2. SOAP消息交换模型
SOAP消息从发送方到接收方是单向传送,但正如上面显示的,SOAP消息经常以请求/应答的方式实现。

SOAP实现可以通过开发特定网络系统的特性来优化。例如,HTTP绑定(见第6节)使SOAP应答消息以HTTP应答的方式传输,并使用同一个连接返回请求。

不管SOAP被绑定到哪个协议,SOAP消息采用所谓的”消息路径“发送,这使在终节点之外的中间节点可以处理消息。

一个接收SOAP消息的SOAP应用程序必须按顺序执行以下的动作来处理消息:

识别应用程序想要的SOAP消息的所有部分 (见4.2.2节)
检验应用程序是否支持第一步中识别的消息中所有必需部分并处理它。如果不支持,则丢弃消息(见4.4节)。在不影响处理结果的情况下,处理器可能忽略第一步中识别出的可选部分。
如果这个SOAP应用程序不是这个消息的最终目的地,则在转发消息之前删除第一步中识别出来的所有部分。
为了正确处理一条消息或者消息的一部分,SOAP处理器需要理解:所用的交换方式(单向,请求/应答,多路发送等等),这种方式下接收者的任务,RPC机制(如果有的话)的使用(如第7节中所述),数据的表现方法或编码,还有其它必需的语义。

尽管属性比如SOAP encodingstyle(见4.1.1节)可以用于描述一个消息的某些方面,但这个规范并不强制所有的接收方也必须有同样的属性并取同样的属性值。举个例子,某一特定的应用可能知道一个元素表示一条遵循第7节约定的RPC请求,但是另外一些应用可能认为指向该元素的所有消息都用单向传输,而不是类似第7节的请求应答模式。
(译者注:交互双方的SOAP消息并不一定要遵循同样的格式设定,而只需要以一种双方可理解的格式交换信息就可以了)

3. 与XML的关系
所有的SOAP消息都使用XML形式编码(更多有关XML的信息请见[7])

一个SOAP应用程序产生的消息中,所有由SOAP定义的元素和属性中必须包括正确的名域。SOAP应用程序必须能够处理它接收到的消息中的SOAP名域(见4.4节),并且它可以处理没有SOAP名域的SOAP消息,就象它们有正确的名域一样。

SOAP定义了两个名域(更多有关XML名域的信息请见[8])

SOAP封装的名域标志符是"http://schemas.xmlsoap.org/soap/envelope/"
SOAP的编码规则的名域标志符是"http://schemas.xmlsoap.org/soap/encoding/"
SOAP消息中不能包含文档类型声明,也不能包括消息处理指令。[7]

SOAP使用"ID"类型"id"属性来指定一个元素的唯一的标志符,同时该属性是局部的和无需校验的。SOAP使用"uri-reference"类型的"href"属性指定对这个值的引用,同时该属性是局部的和无需校验的。这样就遵从了XML规范[7],XML Schema规范[11]和XML连接语言规范[9]的风格。

除了SOAP mustUnderstand 属性(见4.2.3节)和SOAP actor属性(见4.2.2节)之外,一般允许属性和它们的值出现在XML文档实例或Schema中(两者效果相同)。也就是说,在DTD或Schema中声明一个缺省值或固定值和在XML文档实例中设置它的值在语义上相同。

4. SOAP封装
SOAP消息是一个XML文档,包括一个必需的SOAP封装,一个可选的SOAP头和一个必需的SOAP体。在这篇规范剩余部分中,提到SOAP消息时就是指这个XML文档。这一节中定义的元素和属性的名域标志符为:"http://schemas.xmlsoap.org/soap/envelope/" 。一个SOAP消息包括以下部分:

在表示这个消息的XML文档中,封装是顶层元素。
应用SOAP交换信息的各方是分散的且没有预先协定,SOAP头提供了向SOAP消息中添加关于这条SOAP消息的某些要素(feature)的机制。SOAP定义了少量的属性用来表明这项要素(feature)是否可选以及由谁来处理。(见4.2节)
SOAP体是包含消息的最终接收者想要的信息的容器(见4.3节)。SOAP为SOAP体定义了一个Fault元素用来报告错误信息。
语法规则如下所示:

封装
元素名是 "Envelope"
在SOAP消息中必须出现。
可以包含名域声明和附加属性。如果包含附加属性,这些属性必须限定名域。类似的,"Envelope"可以包含附加子元素,这些也必须限定名域且跟在SOAP体元素之后。
SOAP头 (见4.2节)
元素名是"Header"
在SOAP消息中可能出现。如果出现的话,必须是SOAP 封装元素的第一个直接子元素。
SOAP头可以包含多个条目,每个都是SOAP头元素的直接子元素。所有SOAP头的直接子元素都必须限定名域。
SOAP体 (见4.3节)
元素名是"Body"
在SOAP消息中必须出现且必须是SOAP封装元素的直接子元素。它必须直接跟在SOAP头元素(如果有的话)之后。否则它必须是SOAP封装元素的第一个直接子元素。
SOAP体可以包括多个条目,每个条目必须是SOAP体元素的直接子元素。SOAP体元素的直接子元素可以限定名域。SOAP定义了SOAP Fault元素来表示错误信息。(见4.4节).
4.1.1 SOAP encodingStyle 属性
EncodingStyle全局属性用来表示SOAP消息的序列化规则。这个属性可以在任何元素中出现,作用范围与名域声明的作用范围很相似,为这个元素的内容和它的所有没有重载此属性的子元素。SOAP消息没有定义缺省编码。

属性值是一个或多个URI的顺序列表,每个URI确定了一种或多种序列化规则,用来不同程度反序列化SOAP消息,举例如下:

"http://schemas.xmlsoap.org/soap/encoding/"
"http://my.host/encoding/restricted http://my.host/encoding/"
""

第5节中定义的序列化规则由URI"http://schemas.xmlsoap.org/soap/encoding/" 确定。使用这个特定序列化规则的消息应该用encodingStyle属性说明这一点。另外,所有以"http://schemas.xmlsoap.org/soap/encoding/"开头的URI中的序列化规则与第5节中定义的SOAP编码规则相一致。

一个零长度的URI("")明确显示所含元素没有任何编码形式。这可以用来取消上一级元素的所有编码声明。

4.1.2 封装版本模型
SOAP没有定义常规的基于主版本号和辅版本号的版本形式。SOAP消息必须有一个封装元素与名域"http://schemas.xmlsoap.org/soap/envelope/"关联。如果SOAP应用程序接收到的SOAP消息中的SOAP封装元素与其他的名域关联,则视为版本错误,应用程序必须丢弃这个消息。如果消息是通过HTTP之类的请求/应答协议收到的,应用程序必须回答一个SOAP VersionMismatch 错误信息(见4.4节)。

4.2 SOAP头
SOAP为相互通信的团体之间提供了一种很灵活的机制:在无须预先协定的情况下,以分散但标准的方式扩展消息。可以在SOAP头中添加条目实现这种扩展,典型的例子有认证,事务管理,支付等等。

头元素编码为SOAP封装元素的第一个直接子元素。头元素的所有直接子元素称作条目。

条目的编码规则如下:

一个条目有它的完整的元素名(包括名域URI和局部名)确定。SOAP头的直接子元素必须有名域限制。
SOAP encodingStyle属性可以用来指示条目所用的编码形式(见4.1.1节)
SOAP mustUnderstand属性(见4.2.3节)和SOAP actor属性(见4.2.2节)可以用来指示如何处理这个条目以及由谁来处理。(见4.2.1节)
4.2.1 使用头属性
这一节中定义的SOAP头属性确定了SOAP消息的接收者应该怎样按第2节中所述的方式处理消息。产生SOAP消息的SOAP应用程序,应该仅仅在SOAP头元素的直接子元素中使用这些SOAP头属性。SOAP消息的接收者必须忽略所有不在SOAP头元素的直接子元素中SOAP头属性。

下面的例子是一个SOAP头,包括一个元素标志符"Transaction","mustUnderstand"取值为"1"和数值5。这应该以如下方式编码:

<SOAP-ENV:Header>
<t:Transaction
xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1">
5
</t:Transaction>
</SOAP-ENV:Header>

4.2.2 SOAP actor属性
一个SOAP消息从始节点到终节点的过程中,可能沿着消息路径经过一系列SOAP中间节点。一个SOAP中间节点是一个可以接收转发SOAP消息的应用程序。中间节点和终节点由URI区分。

可能SOAP消息的终节点并不需要所有部分,而在消息路径上的一个和几个中间节点可能需要这些内容。头元素的接收者扮演的角色类似于一个过滤器,防止这些只发给本接受者的消息部分扩散到其它节点。即一个头元素的接收者必须不转发这些头元素到SOAP消息路径上的下一个应用程序。同样的,接收者可能插入一个相似的头元素。

SOAP actor全局属性可以用于指示头元素的接收者。SOAP actor属性的值是一个URI。URI "http://schemas.xmlsoap.org/soap/actor/next"指出了第一个处理这个消息的SOAP应用程序需要这个头元素。这类似于HTTP头中用Connection域表示hop-by-hop范围模型。

省略SOAP actor属性表示接收者是SOAP消息的终节点。

如果这个属性要生效,它必须出现在SOAP消息实例中。(见第3节和4.2.1节)

4.2.3 SOAP mustUnderstand属性
SOAP mustUnderstand全局属性用来指示接受者在处理消息时这个条目是否必须处理。条目的接收者由SOAP actor属性定义(见4.2.2节)。MustUnderstand属性的值是"1" 或 "0"。缺少SOAP mustUnderstand属性在语义上等同于它的值为"0"。

如果一个头元素的SOAP mustUnderstand属性的值是"1",那么条目的接受者必须或者遵守语义(如以元素的全名传送)并按照语义正确的处理,或者放弃处理消息(见4.4节)。

SOAP mustUnderstand 属性考虑了消息演变的准确性(robust evolution)。必须假定包含SOAP mustUnderstand属性且值为"1"的元素以某种方式修改了它们的父元素或同层元素的语义。以这种方式连接元素确保了语义上的变化不会被那些不能完全理解它的接收者忽略。

如果这个属性要生效,它必须出现在SOAP消息实例中。(见第3节和4.2.1节)

4.3 SOAP体
SOAP体元素提供了一个简单的机制,使消息的最终接收者能交换必要的信息。使用体元素的典型情况包括配置RPC请求和错误报告。

体元素编码为SOAP封装元素的直接子元素。如果已经有一个头元素,那么体元素必须紧跟在头元素之后,否则它必须是SOAP封装元素的第一个直接子元素。

体元素的所有直接子元素称作体条目,每个体条目在SOAP体元素中编码为一个独立的元素。

条目的编码规则如下:

一个条目由它的元素全名(包括名域URI和局部名)确定。SOAP体元素的直接子元素可能是名域限制的。
SOAP encodingStyle属性可能用来指示条目(见4.1.1节)的编码方式。
SOAP定义了一个Fault条目用来报告错误信息。(见4.4节)

4.3.1 SOAP头和体的关系
虽然头和体定义为独立的元素,它们实际上是有关系的。体条目和头条目的关系如下:体条目在语义上等同于actor属性为缺省值且mustUnderstand属性值为"1"的头条目。不使用actor属性则表示缺省的actor。(见4.2.2节)

4.4 SOAP错误
SOAP错误元素用于在SOAP消息中携带错误和(或)状态信息。如果有SOAP错误元素,它必须以以体条目的方式出现,并且在一个体元素中最多出现一次。

SOAP错误元素定义了以下四个子元素:

faultcode
faultcode元素给软件提供了一个识别此错误的算法机制。SOAP错误元素必须有faultcode子元素,并且它的值必须是一个合法的名(在[8]节定义)。SOAP定义一些SOAP faultcode描述基本的SOAP错误(见4.4.1节)。
faultstring
faultstring元素提供了一个错误解释,而不是为了软件处理。faultstring元素类似于HTTP中定义(见[5],第6.1节)的'Reason-Phrase'。SOAP错误元素必须有faultstring子元素,并且它应该提供一些错误本质的解释信息。
faultactor
faultactor元素提供了在消息路径上是谁导致了错误发生的信息(见第2节)。它类似于SOAP actor属性(见4.2.2节),只是SOAP actor指的是头条目的目的地,faultactor指的是错误的来源。faultactor属性的值是用来区分错误来源的URI。不是SOAP消息的最终目的地的应用程序必须在SOAP Fault元素中包含faultactor元素。消息的最终目的地可以使用faultactor元素明确的指示是它产生了这个错误(参见下面的detail元素)
detail
detail元素用来携带与Body元素有关的应用程序所要的错误信息。如果Body元素的内容不能被成功的处理,则必须包含detail子元素。它不能用来携带属于头条目的错误信息。头条目的详细出错信息必须由头条目携带。
Fault元素中没有detail元素表示这个错误与Body元素的处理无关。在有错误的时候,这可以用来区分Body元素有没有被正确的处理。

detail元素的所有直接子元素称作detail条目,并且每个detail条目在detail元素中编码为独立的元素。

detail条目的编码规则如下(参见例10):
一个detail条目由它的元素全名(包括名域URI和局部名)确定。SOAP体元素的直接子元素可能是名域限制的。
SOAP encodingStyle属性可能用来指示detail条目(见4.1.1节)的编码方式。
也可以有其它的Fault子元素,只要它们是名域限制的。


soap规范(2)

发表人:lewang | 发表时间: 2006年五月13日, 13:21

4.4.1 SOAP 错误代码
在描述这个规范中定义的错误时,这一节中定义的Faultcode值必须用在faultcode元素中。这些faultcode值得名域标志符为"http://schemas.xmlsoap.org/soap/envelope/"。定义这个规范之外的方法时推荐(不要求)使用这个名域。

缺省的SOAP faultcode值以可扩展的方式定义,允许定义新的SOAP faultcode值,并与现有的faultcode值向后兼容。使用的机制类似于HTTP中定义的1xx, 2xx, 3xx等基本的状态类(见[5]第10节),不过,它们定义为XML合法名(见 [8] 第3节 ),而不是整数。字符"."(点)作为faultcode的分隔符,点左边的错误代码比右边的错误代码更为普通。如:

Client.Authentication

这篇文档中定义的faultcode值是:

名称 含义
VersionMismatch 处理方发现SOAP封装元素有不合法的名域(见4.1.2节)
MustUnderstand 处理方不理解或者不服从一个包含值为"1"的mustUnderstand属性的 SOAP头元素的直接子元素。(见4.2.3节)
Client Client错误类表示消息的格式错误或者不包含适当的正确信息。例如,消息可能缺少正确的认证和支付信息。一般地,它表示消息不能不作修改就重发。参见4.4节SOAP Fault detail子元素的描述。
Server Server错误类表示由于消息的处理过程而不是消息的内容本身使得消息消息不能正确的处理。例如,处理消息时可能要与其它处理器通信,但它没有响应。这个消息可能在迟一点的时间处理成功。 SOAP Fault子元素的详细信息参见4.4节

5. SOAP编码
SOAP编码格式基于一个简单的类型系统,概括了程序语言,数据库和半结构化数据等类型系统的共同特性。一个类型或者是一个简单的(标量的)类型,或者是由几个部分组合而成的复合类型,其中每个部分都有自己的类型。以下将详细描述这些类型。这一节定义了类型化对象的序列化规则。它分两个层次。首先,给定一个与类型系统的符号系统一致的Schema(译者注:这里的schema不是符合XML语法的schema,而仅仅表示广义的用于表示消息结构的定义方式),就构造了XML语法的Schema。然后,给定一个类型系统的Schema和与这个Schema一致的特定的值,就构造了一个XML文档实例。反之,给定一个依照这些规则产生的XML文档实例和初始的Schema,就可以构造初始值的一个副本。

这一节中定义的元素和属性的名域标志符为"http://schemas.xmlsoap.org/soap/encoding/"。下面的例子都假定在上一层的元素中声明了名域。

鼓励使用这一节中描述的数据模型和编码方式,但也可以在SOAP中使用其他的数据模型和编码方式。(见4.1.1节)

5.1 XML中的编码类型规则
XML允许非常灵活的数据编码方式。SOAP定义了一个较小的规则集合。这一节在总的层次上定义了这些编码规则,下一节将描述特定类型的编码规则的细节。这一节定义的编码规则可以与第7节中所述的RPC调用和应答映射结合使用。

下面的术语用来描述编码规则:

一个"value"是一个字符串,类型(数字,日期,枚举等等)的名或是几个简单值的组合。所有的值都有特定的类型。
一个"simple value"没有名部分, 如特定的字符串,整数,枚举值等等。
一个"compound value"是相关的值的结合,如定单,股票报表,街道地址等等。
在"compound value"中,每个相关的值都潜在的以名,序数或这两者来区分。这叫作"accessor"。复合值的例子有定单和股票报表等等。数组也是复合值。在复合值中,多个accessor有相同的名是允许的,例如RDF就是这样做的。
一个"array"是一个复合值,成员值按照在数组中的位置相互区分。
一个"struct"也是一个复合值,成员值之间的唯一区别是accessor名,accessor名互不相同。
一个"simple type"是简单值的类,如叫做"string" "integer"的类,还有枚举类等等。
一个"compound type"是复合值的类。复合类型的例子有定单类,它们有相同的accessor名(shipTo, totalCost等),但可能会有不同的值(可能以后被设置为确定的值)。
在复合类型中,如果类型内的accessor名互不相同,但是可能与其他类型中的accessor名相同,即,accessor名加上类型名形成一个唯一的标志符,这个名叫作"局部范围名"。如果名是直接或间接的基于URI的一部分,那么不管它出现在什么类型中,这个名本身就可以唯一标志这个accessor,这样的名叫作"全局范围名"。
给定了schema中相关的值的序列化信息,就可能确定某些值只与某个accessor的一个实例有关。其它情况下则无法确定。当且仅当一个accessor引用一个值,这个值才能被视为"single-reference",如果有不止一个accessor引用它,那么就将它视为"multi-reference"。注意,可能一个确定的值在一个schema中是"single-reference",而在另一个schema中是"multi-reference"。
在语句构成上,一个元素可能是"independent" 或 "embedded"。一个独立的元素指出现在序列化最顶层的任何元素。所有其它元素都是嵌入元素。
虽然用xsi:type属性可以使值的结构和类型变为自描述的,但是序列化规则允许值的类型仅仅参照schema而定。这样的schema可能使用"XML Schema Part 1: Structures" [10]和"XML Schema Part 2: Datatypes" [11]中描述的符号系统,也可能使用其它符号系统。注意,虽然序列化规则可以用于除了数组和结构之外的复合类型,但是许多schema仅仅包含数组和结构类型。

序列化规则如下:

所有的值以元素内容的形式表示。一个multi-reference值必须表示为一个独立元素的内容,而一个single-reference值最好不要这样表示(也可以这样表示)。
对于每个具有值的元素,值的类型时必须用下述三种方式之一描述:(a)所属元素实例有xsi:type属性 (b)所属元素是一个有SOAP-ENC:arrayType 属性(该属性可能是缺省的)的元素的子元素,或者(c)所属元素的名具有特定的类型,类型可以由schema确定。
一个简单值表示为字符数据,即没有任何子元素。每个简单值必须具有一个类型,这个类型或者是XML Schemas Specification, part 2 [11]有的类型,或者具有源类型(参见5.2节)。
一个复合值编码成一个元素的序列,每个accessor用一个嵌入元素表示,该元素的元素名和accessor的名一致。如果accessor的名是局部于其所属的类型的,则该元素的元素名不是合格的,否则对应的元素名是合格的。(参见5.4节)
一个multi-reference的简单值或复合值编码成一个独立的元素,这个元素包含一个局部的无需校验的属性,属性名为"id",类型为"ID"(依照XML Specification [7])。值的每个accessor对应一个空元素,该元素有一个局部的,无需校验的属性,属性名为"href",类型为" uri-reference "(依照XML Schema Specification [11]),"href"属性的值引用了相对应的独立元素的URI标志符。
字符串和字符数组表示为multi-reference的简单类型,但是特殊的规则使它们在普通的情况下能被更有效的表示(参见5.2.1节和5.2.3节)。字符串和字符数组值的accessor可能有一个名字为"id",类型为"ID"(依照XML Specification [7])的属性。如果这样,所有这个值的所有其它accessor编码成一个空元素,这个元素有一个局部的,无需校验的属性,属性名为"href",类型为" uri-reference "(依照XML Schema Specification [11]),"href"属性的值引用了包含这个值的元素的URI标志符。
编码时允许一个值有多个引用,就像多个不同的值有多个引用一样,但这仅在从上下文可以知道这个XML文档实例的含义没有改变时才可使用。
数组是复合值(参见5.4.2节)。SOAP数组定义为具有类型"SOAP-ENC:Array"或从它衍生的类型.

SOAP数组可以时一维或多维,它们的成员以序数位置相互区分。一个数组值表示为反映这个数组的一系列元素,数组成员按升序出现。对多维数组来说,右边的这一维变化最快。每个成员元素命名为一个独立元素。(见规则2)

SOAP数组可以是single-reference 或multi-reference值,因此可以表示为嵌入元素或独立元素的内容。

SOAP数组必须包含一个"SOAP-ENC:arrayType"属性,它的值指定了包含元素的类型和数组的维数。"SOAP-ENC:arrayType"属性的值定义如下:
arrayTypeValue = atype asize
atype = QName *( rank )
rank = "[" *( "," ) "]"
asize = "[" #length "]"
length = 1*DIGIT

"atype"结构是被包含元素的类型名,它表示为QName并且作为类型限制在XML元素声明的"type"属性中出现(这意味着被包含元素的所有值都要与该类型一致,即在SOAP-ENC:arrayType中引用的类型必须是每个数组成员的类型或超类型)。在arrays of arrays or "jagged arrays"的情况下,类型组件编码为"innermost"类型且在从第一层开始的嵌套数组的每一层中,类型名后都跟随一个rank结构。多维数组编码时从第一维起,每一维之间用逗号隔开。

"asize"结构包含一个以逗号分隔的列表,数值0,1或其它整数表示数组每一维的长度。整数0表示没有指定详细的大小,但是可能在检查数组实际成员的大小后确定。

例如,一个5个成员的整型数组的arrayTypeValue值为"int[][5]",它的atype值是int[]",asize值是"[5]"。同样,一个3个成员的两维整型数组的arrayTypeValue值为"int[,][3]",它的atype值是int[,]",asize值是"[3]"。

一个SOAP数组成员可能包含一个"SOAP-ENC:offset"属性表示这一项在整个数组中的位置偏移值。这被用来指示一个部分储值数组(见5.4.2.1节)的位置偏移值。同样,一个数组成员可能包含一个"SOAP-ENC:position"属性表示这一项在整个数组中的位置,这被用来描述稀疏数组(见5.4.2.2节)的成员。"SOAP-ENC:offset" 和"SOAP-ENC:position"属性值的定义如下:

arrayPoint = "[" #length "]"
偏移值和位置从0开始

NULL值或缺省值可能通过省略accssor元素来表示。NULL值也可能通过一个包含值为'1'的xsi:null属性的accssor元素来表示,其它的依赖于应用程序的属性和值也可能用来表示NULL值。
注意,规则2允许独立的元素和数组成员名不同于值类型的元素。

5.2 简单类型
SOAP采用了"XML Schema Part 2: Datatypes"规范[11]"Built-in datatypes"节中的所有类型作为简单类型,包括值和取值范围。例如:

类型 举例
int 58502
float 314159265358979E+1
negativeInteger -32768
string Louis "Satchmo" Armstrong

在XML Schema规范中声明的数据类型可以直接用在元素schema中,也可以使用从这些类型衍生的新类型。一个schema和对应的具有这些类型的元素的数据实例的例子如下所示:

<element name="age" type="int"/>
<element name="height" type="float"/>
<element name="displacement" type="negativeInteger"/>
<element name="color">
<simpleType base="xsd:string">
<enumeration value="Green"/>
<enumeration value="Blue"/>
</simpleType>
</element>

<age>45</age>
<height>5.9</height>
<displacement>-450</displacement>
<color>Blue</color>

所有简单值必须编码为元素的内容,它的类型或者在"XML Schema Part 2: Datatypes"规范[11]中定义过,或者是基于一个用XML Schema规范提供的机制能推衍生出的类型。

如果一个简单值编码为独立元素或异质数组成员,那么有一个对应于数据类型的元素声明将会很方便。因为"XML Schema Part 2: Datatypes"规范[11]包括了类型定义,但是不包括对应的元素声明,SOAP-ENC schema和名域为每个简单数据类型声明了一个元素,如

<SOAP-ENC:int id="int1">45</SOAP-ENC:int>
5.2.1 字符串
字符串数据类型的定义在"XML Schema Part 2: Datatypes"规范[11]中。注意,这不同于许多数据库和程序语言中的"string"类型,特别的,字符串数据类型可能禁止某些在那些语言中允许的字符。(这些值必须用xsd:string之外的数据类型表示)

一个字符串可能编码为一个single-reference 或 multi-reference值。

包含字符串值的元素可能有一个"id"属性。附加的accessor元素可能有对应的"href"属性。

例如,同一字符串的两个accessor可能以如下形式出现:

<greeting id="String-0">Hello</greeting>
<salutation href="#String-0"/>
但是,如果两个accessor参考同一字符串实例(或字符串的子类型),这不是一个实质问题,它们可以编码为两个single-reference值,如下所示:

<greeting>Hello</greeting>
<salutation>Hello</salutation>
这个例子的schema片断如下所示:

<element name="greeting" type="SOAP-ENC:string"/>
<element name="salutation" type="SOAP-ENC:string"/>

在这个例子中,SOAP-ENC:string类型用作元素的类型,这是声明数据类型是"xsd:string"且允许"id" 和"href"属性的元素的简便方法。精确定义参见SOAP编码schema。Schemas可以使用这些源自SOAP编码schema的声明,但也可以不这样做。

5.2.2 Enumerations
"XML Schema Part 2: Datatypes"规范 [11] 定义了"enumeration."机制。SOAP数据模型直接采用了这种机制。但是,由于程序语言和其它语言在定义枚举时通常有些不同,所以我们在这里详细阐述了它的概念并描述了一个列表成员的可能取的值是如何编码的。

"Enumeration"作为一个概念表示不同的名字的集合。一个特定的枚举就是对应于特定的基类型的不同的值的列表。例如,颜色集合("Green", "Blue", "Brown")可以定义为基于字符串类型的枚举,("1", "3", "5")可能是一个基于整型数的枚举,等等。"XML Schema Part 2: Datatypes" [11]支持除了布尔型以外所有简单类型的枚举。"XML Schema Part 1: Structures"规范[10]的语言可以用来定义枚举类型。如果schema由另一个没有特定基类型适用的符号系统生成,就使用"string"。在下面schema的例子中,"EyeColor"定义为字符串,可能的值是"Green", "Blue", 或"Brown"的枚举,数据实例按照schema显示如下。

<element name="EyeColor" type="tns:EyeColor"/>
<simpleType name="EyeColor" base="xsd:string">
<enumeration value="Green"/>
<enumeration value="Blue"/>
<enumeration value="Brown"/>
</simpleType>

<Person>
<Name>Henry Ford</Name>
<Age>32</Age>
<EyeColor>Brown</EyeColor>
</Person>

5.2.3 字符数组
一个字符数组可能编码为single-reference 或multi-reference值。字符数组的编码规则与字符串的编码规则类似。

特别的,包含字符数组的元素值可能由一个"id"属性,附加的accssor元素可能有相应的"href"属性。

推荐使用定义在XML Schemas [10][11]中的'base64'编码(使用在2045 [13]中定义的base64编码算法)表示模糊字符数组。不过,由于行长度(line length)的限制,通常在MIME中应用base64编码,SOAP中一般不应用base64编码。但是提供了"SOAP-ENC:base64"子类型使之能用于SOAP。

<picture xsi:type="SOAP-ENC:base64">
aG93IG5vDyBicm73biBjb3cNCg==
</picture>

5.3 多态accessor
许多语言允许能够多态访问多种类型值的accessor,每种类型在运行时可用。一个多态accessor实例必须包含一个"xsi:type"属性描述实际值的类型。

例如,一个名为"cost"类型值为"xsd:float"的多态accessor编码如下:

<cost xsi:type="xsd:float">29.95</cost>
与之对比,类型值不变的accessor编码如下:

<cost>29.95</cost>
5.4 Compound types复合类型
SOAP定义了与下列常在程序语言中出现的结构性模式对应的类型:

结构
一个"struct"是一个复合值,它的成员值的唯一区别是accessor名称,任意两个accessor名称都不相同。
数组
一个"array"是一个复合值,它的成员值的唯一区别是序数位置。
SOAP也允许结构和数组之外的其它数据的序列化,例如Directed-Labeled-Graph Data Model之类的数据中,单个节点有许多不同的accssor,有些不止出现一次。SOAP序列化规则不要求底层的数据模型在accssor之间区分次序,但如果有这样的次序的话,这些accssor必须按照这个顺序编码。

5.4.1 复合值,结构和值引用
复合值的成员编码为accessor元素。当accessor由名区分时(如结构),accessor名即作为元素名。名局部于类型的accessor有不受限的名,其它的accessor则有受限的名。

下面的例子是类型为"Book"的结构:

<e:Book>
<author>Henry Ford</author>
<preface>Prefatory text</preface>
<intro>This is a book.</intro>
</e:Book>

以下是描述上面结构的schema片断:

<element name="Book">
<complexType>
<element name="author" type="xsd:string"/>
<element name="preface" type="xsd:string"/>
<element name="intro" type="xsd:string"/>
</complexType>
</e:Book>


Overloading overriding runtime type and object orientation (2)

发表人:lewang | 发表时间: 2006年五月13日, 13:20

Objective 3)
Write code to construct instances of any concrete class including normal top level classes inner classes static inner classes and anonymous inner classes.
Inner Classes
· A class can be declared in any scope. Classes defined inside of other classes are known as nested classes. There are four categories of nested classes.
Top-level nested classes / interfaces
· Declared as a class member with static modifier.
· Just like other static features of a class. Can be accessed / instantiated without an instance of the outer class. Can access only static members of outer class. Can’t access non-static instance variables or methods.
· Very much like any-other package level class / interface. Provide an extension to packaging by the modified naming scheme at the top level.
· Classes can declare both static and non-static members.
· Any accessibility modifier can be specified.
· Nested interfaces are implicitly static (static modifier also can be specified). They can have any accessibility modifier. There are no non-static inner, local or anonymous interfaces.
Non-static inner classes
· Declared as a class member without static.
· An instance of a non-static inner class can exist only with an instance of its enclosing class. So it always has to be created within a context of an outer instance.
Outer.Inner i = new Outer().new Inner();
· Just like other non-static features of a class. Can access all the features (even private) of the enclosing outer class. Have an implicit reference to the enclosing instance.
· Cannot have any static members.
· Can have any access modifier.
Local classes
· Defined inside a block (could be a method, a constructor, a local block, a static initializer or an instance initializer). Cannot be specified with static modifier.
· Cannot have any access modifier (since they are effectively local to the block)
· Cannot declare any static members.(Even declared in a static context)
· Can access all the features of the enclosing class (because they are defined inside the method of the class) but can access only final variables defined inside the method (including method arguments). This is because the class can outlive the method, but the method local variables will go out of scope – in case of final variables, compiler makes a copy of those variables to be used by the class. (New meaning for final)
· Since the names of local classes are not visible outside the local context, references of these classes cannot be declared outside. So their functionality could be accessed only via super-class references (either interfaces or classes). Objects of those class types are created inside methods and returned as super-class type references to the outside world. This is the reason that they can only access final variables within the local block. That way, the value of the variable can be always made available to the objects returned from the local context to outside world.
· Cannot be specified with static modifier. But if they are declared inside a static context such as a static method or a static initializer, they become static classes. They can only access static members of the enclosing class and local final variables. But this doesn’t mean they cannot access any non-static features inherited from super classes. These features are their own, obtained via the inheritance hierarchy. They can be accessed normally with ‘this’ or ‘super’.
Anonymous classes
· Anonymous classes are defined where they are constructed. They can be created wherever a reference expression can be used.
· An anonymous class is never abstract . An anonymous class is always an inner class; it is never static . An anonymous class is always implicitly final.
· Anonymous classes cannot have explicit constructors. Instance initializers can be used to achieve the functionality of a constructor.
· Anonymous classes can implement an interface (implicit extension of Object) or explicitly extend a class. Cannot do both.
Syntax: new interfacename() { } or new classname(can take arg.) { }
· Keywords implements and extends are not used in anonymous classes.
· Abstract classes can be specified in the creation of an anonymous class. The new class is a concrete class, which automatically extends the abstract class.
· Discussion for local classes on static/non-static context, accessing enclosing variables, and declaring static variables also holds good for anonymous classes. In other words, anonymous classes cannot be specified with static, but based on the context, they could become static classes. In any case, anonymous classes are not allowed to declare static members. Based on the context, non-static/static features of outer classes are available to anonymous classes. Local final variables are always available to them.
· E.g.
btn.addActionListener(
new ActionListener() {
void ActionPerformed() {System.out.println(s);}
}
)


· One enclosing class can have multiple instances of inner classes.
· Inner classes can have synchronous methods. But calling those methods obtains the lock for inner object only, not the outer object. If you need to synchronize an inner class method based on outer object, outer object lock must be obtained explicitly. Locks on inner object and outer object are independent.
· Nested classes can extend any class or can implement any interface. No restrictions.
· All nested classes (except anonymous classes) can be abstract or final.
· Classes can be nested to any depth. Top-level static classes can be nested only within other static top-level classes or interfaces. Deeply nested classes also have access to all variables of the outer-most enclosing class (as well the immediate enclosing class’s)
· Member inner classes can be forward referenced. Local inner classes cannot be.(?)
· Outer class variables are accessible within the inner class, but they are not inherited. They don’t become members of the inner class. This is different from inheritance. (Outer class cannot be referred using ‘super’, and outer class variables cannot be accessed using ‘this’)
· An inner class variable can shadow an outer class variable. If the inner class is sub-classed within the same outer class, the variable has to be qualified explicitly in the sub-class. To fully qualify the variable, use (classname.this.variablename). If we don’t correctly qualify the variable, a compiler error will occur. (Note that this does not happen in multiple levels of inheritance where an upper-most super-class’s variable is silently shadowed by the most recent super-class variable or in multiple levels of nested inner classes where an inner-most class’s variable silently shadows an outer-most class’s variable. Problem comes only when these two hierarchy chains (inheritance and containment) clash.)
· If the inner class is sub-classed outside of the outer class (only possible with top-level nested classes) explicit qualification is not needed (it becomes regular class inheritance)
· Inner class cannot have some name as any of its enclosing class.

Entity Declaration Context Accessibility Modifiers Outer instance Direct Access to enclosing context Defines static or non-static members
Package level class As package member Public or default No N/A Both static and non-static
Top level nested class (static) As static class member All No Static members in enclosing context Both static and non-static
Non static inner class As non-static class member All Yes All members in enclosing context Only non-static
Local class (non-static) In block with non-static context None Yes All members in enclosing context + local final variables Only non-static
Local class (static) In block with static context None No Static members in enclosing context + local final variables Only non-static
Anonymous class (non-static) In block with non-static context None Yes All members in enclosing context + local final variables Only non-static
Anonymous class (static) In block with static context None No Static members in enclosing context + local final variables Only non-static
Package level interface As package member Public or default No N/A Static variables and non-static method prototypes
Top level nested interface (static) As static class member All No Static members in enclosing context Static variables and non-static method prototypes


// Example 1

public class InnerInnerTest {
public static void main(String s[]) {
new Outer().new Inner().new InnerInner().new InnerInnerInner().doSomething();
new Outer().new InnerChild().doSomething();
new Outer2().new Inner2().new InnerInner2().doSomething();
new InnerChild2().doSomething();

}
}

class Outer {
String name = "Vel";

class Inner {
String name = "Sharmi";

class InnerInner {

class InnerInnerInner {

public void doSomething() {

// No problem in accessing without full qualification,
// inner-most class variable shadows the outer-most class variable
System.out.println(name); // Prints "Sharmi"
System.out.println(Outer.this.name); // Prints "Vel", explicit reference to Outer

// error, variable is not inherited from the outer class, it can be just accessible
// System.out.println(this.name);
// System.out.println(InnerInner.this.name);
// System.out.println(InnerInnerInner.this.name);

// error, super cannot be used to access outer class.
// super will always refer the parent, in this case Object
// System.out.println(super.name);

System.out.println(Inner.this.name); // Prints "Sharmi", Inner has declared 'name'
}
}
}
}

/* This is an inner class extending an inner class in the same scope */
class InnerChild extends Inner {
public void doSomething() {
// compiler error, explicit qualifier needed
// 'name' is inherited from Inner, Outer's 'name' is also in scope
// System.out.println(name);
System.out.println(Outer.this.name); // prints "Vel", explicit reference to Outer
System.out.println(super.name); // prints "Sharmi", Inner has declared 'name'
System.out.println(this.name); // prints "Sharmi", name is inherited by InnerChild
}
}
}

class Outer2 {
static String name = "Vel";
static class Inner2 {
static String name = "Sharmi";

class InnerInner2 {
public void doSomething() {
System.out.println(name); // prints "Sharmi", inner-most hides outer-most
System.out.println(Outer2.name); // prints "Vel", explicit reference to Outer2's static variable
// System.out.println(this.name); // error, 'name' is not inherited
// System.out.println(super.name); // error, super refers to Object
}
}
}

}

/* This is a stand-alone class extending an inner class */
class InnerChild2 extends Outer2.Inner2 {
public void doSomething() {
System.out.println(name); // prints "Sharmi", Inner2's name is inherited
System.out.println(Outer2.name); // prints "Vel", explicit reference to Outer2's static variable
System.out.println(super.name); // prints "Sharmi", Inner2 has declared 'name'
System.out.println(this.name); // prints "Sharmi", name is inherited by InnerChild2
}
}

// Example 2

public class InnerTest2 {
public static void main(String s[]) {

new OuterClass().doSomething(10, 20);

// This is legal
// OuterClass.InnerClass ic = new OuterClass().new InnerClass();
// ic.doSomething();

// Compiler error, local inner classes cannot be accessed from outside
// OuterClass.LocalInnerClass lic = new OuterClass().new LocalInnerClass();
// lic.doSomething();

new OuterClass().doAnonymous();

}
}

class OuterClass {
final int a = 100;
private String secret = "Nothing serious";

public void doSomething(int arg, final int fa) {
final int x = 100;
int y = 200;

System.out.println(this.getClass() + " - in doSomething");
System.out.print("a = " + a + " secret = " + secret + " arg = " + arg + " fa = " + fa);
System.out.println(" x = " + x + " y = " + y);

// Compiler error, forward reference of local inner class
// new LocalInnerClass().doSomething();

abstract class AncestorLocalInnerClass { } // inner class can be abstract

final class LocalInnerClass extends AncestorLocalInnerClass { // can be final
public void doSomething() {
System.out.println(this.getClass() + " - in doSomething");
System.out.print("a = " + a );
System.out.print(" secret = " + secret);
// System.out.print(" arg = " + arg); // Compiler error, accessing non-final argument
System.out.print(" fa = " + fa);
System.out.println(" x = " + x);
// System.out.println(" y = " + y); // Compiler error, accessing non-final variable
}
}

new InnerClass().doSomething(); // forward reference fine for member inner class
new LocalInnerClass().doSomething();
}

abstract class AncestorInnerClass { }

interface InnerInterface { final int someConstant = 999;} // inner interface

class InnerClass extends AncestorInnerClass implements InnerInterface {
public void doSomething() {
System.out.println(this.getClass() + " - in doSomething");
System.out.println("a = " + a + " secret = " + secret + " someConstant = " + someConstant);
}
}

public void doAnonymous() {
// Anonymous class implementing the inner interface
System.out.println((new InnerInterface() { }).someConstant);

// Anonymous class extending the inner class
( new InnerClass() {
public void doSomething() {
secret = "secret is changed";
super.doSomething();
}
} ).doSomething();
}
}

Thus an abstract class cannot be instantiated and so an object reference cannot be created. Remember that a class that contains any abstract methods the class itself is abstract and cannot be instantiated.

A local class is visible only within it's code block or method.

For class inside interface:(?)
1. The class is always public.
2. The class is always static.
3. the class methods cannot call the methods declared in the interface.

Q1
public class MyClass1 {
public static void main(String argv[]){ }
/*Modifier at XX */ class MyInner {}
}
What modifiers would be legal at XX in the above code?
1) public
2) private
3) static
4) friend


Overloading overriding runtime type and object orientation (1)

发表人:lewang | 发表时间: 2006年五月13日, 13:19

6)Overloading overriding runtime type and object orientation
Objective 1)
State the benefits of encapsulation in object oriented design and write code that implements tightly encapsulated classes and the relationships "is a" and "has a".

Encapsulation involves hiding data of a class and allowing access only through a public interface.

The separation of interface and implementation makes it easier to modify the code within a class without breaking any other code that uses it.

encapsulation, inheritance, polymorphism are three principal characteristics of Object Oriented programming.

Implementing OO relationships
· “is a” relationship is implemented by inheritance (extends keyword)
· “has a” relationship is implemented by providing the class with member variables.

Overloading and Overriding
· Overloading is an example of polymorphism. (operational / parametric)
· Overriding is an example of runtime polymorphism (inclusive)
· A method can have the same name as another method in the same class, provided it forms either a valid overload or override


Objective 2)
Write code to invoke overridden or overloaded methods and parental or overloaded constructors; and describe the effect of invoking these methods.

Overloading Overriding
Signature has to be different. Just a difference in return type is not enough. Signature has to be the same. (including the return type)
Accessibility may vary freely. Overriding methods cannot be more private than the overridden methods.
Exception list may vary freely. Overriding methods may not throw more checked exceptions than the overridden methods. But can throw no exception.
Just the name is reused. Methods are independent methods. Resolved at compile-time based on method signature. Related directly to sub-classing. Overrides the parent class method. Resolved at run-time based on type of the object.
Can call each other by providing appropriate argument list. Overriding method can call overridden method by super.methodName(), this can be used only to access the immediate super-class’s method. super.super won’t work. Also, a class outside the inheritance hierarchy can’t use this technique.
Methods can be static or non-static. Since the methods are independent, it doesn’t matter. But if two methods have the same signature, declaring one as static and another as non-static does not provide a valid overload. It’s a compile time error. static methods don’t participate in overriding, since they are resolved at compile time based on the type of reference variable. A static method in a sub-class can’t use ‘super’ (for the same reason that it can’t use ‘this’ for)Remember that a static method can’t be overridden to be non-static and a non-static method can’t be overridden to be static. In other words, a static method and a non-static method cannot have the same name and signature (if signatures are different, it would have formed a valid overload)
There’s no limit on number of overloaded methods a class can have. Each parent class method may be overridden at most once in any sub-class. (That is, you cannot have two identical methods in the same class)


· Variables can also be overridden, it’s known as shadowing or hiding. But, member variable references are resolved at compile-time. So at the runtime, if the class of the object referred by a parent class reference variable, is in fact a sub-class having a shadowing member variable, only the parent class variable is accessed, since it’s already resolved at compile time based on the reference variable type. Only methods are resolved at run-time.

public class Shadow {
public static void main(String s[]) {
S1 s1 = new S1();
S2 s2 = new S2();

System.out.println(s1.s); // prints S1
System.out.println(s1.getS()); // prints S1
System.out.println(s2.s); // prints S2
System.out.println(s2.getS()); // prints S2

s1 = s2;
System.out.println(s1.s); // prints S1, not S2 -
// since variable is resolved at compile time
System.out.println(s1.getS()); // prints S2 -
// since method is resolved at run time
}
}

class S1 {
public String s = "S1";
public String getS() {
return s;
}
}

class S2 extends S1{
public String s = "S2";
public String getS() {
return s;
}
}
In the above code, if we didn’t have the overriding getS() method in the sub-class and if we call the method from sub-class reference variable, the method will return only the super-class member variable value. For explanation, see the following point.

· Also, methods access variables only in context of the class of the object they belong to. If a sub-class method calls explicitly a super class method, the super class method always will access the super-class variable. Super class methods will not access the shadowing variables declared in subclasses because they don’t know about them. (When an object is created, instances of all its super-classes are also created.) But the method accessed will be again subject to dynamic lookup. It is always decided at runtime which implementation is called. (Only static methods are resolved at compile-time)

public class Shadow2 {
String s = "main";
public static void main(String s[]) {
S2 s2 = new S2();
s2.display(); // Produces an output – S1, S2

S1 s1 = new S1();
System.out.println(s1.getS()); // prints S1
System.out.println(s2.getS()); // prints S1 – since super-class method always
// accesses super-class variable

}
}

class S1 {
String s = "S1";
public String getS() {
return s;
}
void display() {
System.out.println(s);
}
}

class S2 extends S1{
String s = "S2";
void display() {
super.display(); // Prints S1
System.out.println(s); // prints S2
}
}

· With OO languages, the class of the object may not be known at compile-time (by virtue of inheritance). JVM from the start is designed to support OO. So, the JVM insures that the method called will be from the real class of the object (not with the variable type declared). This is accomplished by virtual method invocation (late binding). Compiler will form the argument list and produce one method invocation instruction – its job is over. The job of identifying and calling the proper target code is performed by JVM.
· JVM knows about the variable’s real type at any time since when it allocates memory for an object, it also marks the type with it. Objects always know ‘who they are’. This is the basis of instanceof operator.
· Sub-classes can use super keyword to access the shadowed variables in super-classes. This technique allows for accessing only the immediate super-class. super.super is not valid. But casting the ‘this’ reference to classes up above the hierarchy will do the trick. By this way, variables in super-classes above any level can be accessed from a sub-class, since variables are resolved at compile time, when we cast the ‘this’ reference to a super-super-class, the compiler binds the super-super-class variable. But this technique is not possible with methods since methods are resolved always at runtime, and the method gets called depends on the type of object, not the type of reference variable. So it is not at all possible to access a method in a super-super-class from a subclass.

public class ShadowTest {
public static void main(String s[]){
new STChild().demo();
}
}
class STGrandParent {
double wealth = 50000.00;
public double getWealth() {
System.out.println("GrandParent-" + wealth);
return wealth;
}
}
class STParent extends STGrandParent {
double wealth = 100000.00;
public double getWealth() {
System.out.println("Parent-" + wealth);
return wealth;
}
}
class STChild extends STParent {
double wealth = 200000.00;

public double getWealth() {
System.out.println("Child-" + wealth);
return wealth;
}

public void demo() {
getWealth(); // Calls Child method
super.getWealth(); // Calls Parent method
//super.super.getWealth(); // Compiler error, GrandParent method cannot be accessed
((STParent)this).getWealth(); // Calls Child method, due to dynamic method lookup
((STGrandParent)this).getWealth(); // Calls Child method, due to dynamic method
// lookup

System.out.println(wealth); // Prints Child wealth
System.out.println(super.wealth); // Prints Parent wealth
System.out.println(((STParent)(this)).wealth); // Prints Parent wealth
System.out.println(((STGrandParent)(this)).wealth); // Prints GrandParent wealth
}
}

· An inherited method, which was not abstract on the super-class, can be declared abstract in a sub-class (thereby making the sub-class abstract). There is no restriction. In the same token, a subclass can be declared abstract regardless of whether the super-class was abstract or not.
· Private members are not inherited, but they do exist in the sub-classes. Since the private methods are not inherited, they cannot be overridden. A method in a subclass with the same signature as a private method in the super-class is essentially a new method, independent from super-class, since the private method in the super-class is not visible in the sub-class.

public class PrivateTest {
public static void main(String s[]){
new PTSuper().hi(); // Prints always Super
new PTSub().hi(); // Prints Super when subclass doesn't have hi method
// Prints Sub when subclass has hi method
PTSuper sup;
sup = new PTSub();
sup.hi(); // Prints Super when subclass doesn't have hi method
// Prints Sub when subclass has hi method
}
}

class PTSuper {
public void hi() { // Super-class implementation always calls superclass hello
hello();
}
private void hello() { // This method is not inherited by subclasses, but exists in them.
// Commenting out both the methods in the subclass show this.
// The test will then print "hello-Super" for all three calls
// i.e. Always the super-class implementations are called
System.out.println("hello-Super");
}
}

class PTSub extends PTSuper {
public void hi() { // This method overrides super-class hi, calls subclass hello
try {
hello();
}
catch(Exception e) {}

}

void hello() throws Exception { // This method is independent from super-class hello
// Evident from, it's allowed to throw Exception
System.out.println("hello-Sub");
}
}

· Private methods are not overridden, so calls to private methods are resolved at compile time and not subject to dynamic method lookup. See the following example.

public class Poly {
public static void main(String args[]) {
PolyA ref1 = new PolyC();
PolyB ref2 = (PolyB)ref1;

System.out.println(ref2.g()); // This prints 1
// If f() is not private in PolyB, then prints 2
}
}

class PolyA {
private int f() { return 0; }
public int g() { return 3; }
}

class PolyB extends PolyA {
private int f() { return 1; }
public int g() { return f(); }
}

class PolyC extends PolyB {
public int f() { return 2; }
}


jsp源码实例4(cookie)

发表人:lewang | 发表时间: 2006年五月13日, 13:18

package coreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Sets six cookies: three that apply only to the current
* session (regardless of how long that session lasts)
* and three that persist for an hour (regardless of
* whether the browser is restarted).
* <P>
* Taken from Core Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.coreservlets.com/.
* &copy; 2000 Marty Hall; may be freely used or adapted.
*/

public class SetCookies extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
for(int i=0; i<3; i++) {
// Default maxAge is -1, indicating cookie
// applies only to current browsing session.
Cookie cookie = new Cookie("Session-Cookie-" + i,
"Cookie-Value-S" + i);
response.addCookie(cookie);
cookie = new Cookie("Persistent-Cookie-" + i,
"Cookie-Value-P" + i);
// Cookie is valid for an hour, regardless of whether
// user quits browser, reboots computer, or whatever.
cookie.setMaxAge(3600);
response.addCookie(cookie);
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Setting Cookies";
out.println
(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR="#FDF5E6">n" +
"<H1 ALIGN="CENTER">" + title + "</H1>n" +
"There are six cookies associated with this page.n" +
"To see them, visit then" +
"<A HREF="/servlet/coreservlets.ShowCookies">n" +
"<CODE>ShowCookies</CODE> servlet</A>.n" +
"<P>n" +
"Three of the cookies are associated only with then" +
"current session, while three are persistent.n" +
"Quit the browser, restart, and return to then" +
"<CODE>ShowCookies</CODE> servlet to verify thatn" +
"the three long-lived ones persist across sessions.n" +
"</BODY></HTML>");
}
}


jsp源码实例3(获取jsp各种参数)

发表人:lewang | 发表时间: 2006年五月13日, 13:18

package coreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Creates a table showing the current value of each
* of the standard CGI variables.
* <P>
* Taken from Core Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.coreservlets.com/.
* &copy; 2000 Marty Hall; may be freely used or adapted.
*/

public class ShowCGIVariables extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String[][] variables =
{ { "AUTH_TYPE", request.getAuthType() },
{ "CONTENT_LENGTH",
String.valueOf(request.getContentLength()) },
{ "CONTENT_TYPE", request.getContentType() },
{ "DOCUMENT_ROOT",
getServletContext().getRealPath("/") },
{ "PATH_INFO", request.getPathInfo() },
{ "PATH_TRANSLATED", request.getPathTranslated() },
{ "QUERY_STRING", request.getQueryString() },
{ "REMOTE_ADDR", request.getRemoteAddr() },
{ "REMOTE_HOST", request.getRemoteHost() },
{ "REMOTE_USER", request.getRemoteUser() },
{ "REQUEST_METHOD", request.getMethod() },
{ "SCRIPT_NAME", request.getServletPath() },
{ "SERVER_NAME", request.getServerName() },
{ "SERVER_PORT",
String.valueOf(request.getServerPort()) },
{ "SERVER_PROTOCOL", request.getProtocol() },
{ "SERVER_SOFTWARE",
getServletContext().getServerInfo() }
};
String title = "Servlet Example: Showing CGI Variables";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR="#FDF5E6">n" +
"<H1 ALIGN="CENTER">" + title + "</H1>n" +
"<TABLE BORDER=1 ALIGN="CENTER">n" +
"<TR BGCOLOR="#FFAD00">n" +
"<TH>CGI Variable Name<TH>Value");
for(int i=0; i<variables.length; i++) {
String varName = variables[i][0];
String varValue = variables[i][1];
if (varValue == null)
varValue = "<I>Not specified</I>";
out.println("<TR><TD>" + varName + "<TD>" + varValue);
}
out.println("</TABLE></BODY></HTML>");
}

/** POST and GET requests handled identically. */

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


jsp源码实例4(搜索引擎)

发表人:lewang | 发表时间: 2006年五月13日, 13:18

package coreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;


public class SearchEngines extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String searchString = request.getParameter("searchString");
if ((searchString == null) ||
(searchString.length() == 0)) {
reportProblem(response, "Missing search string.");
return;
}
// The URLEncoder changes spaces to "+" signs and other
// non-alphanumeric characters to "%XY", where XY is the
// hex value of the ASCII (or ISO Latin-1) character.
// Browsers always URL-encode form values, so the
// getParameter method decodes automatically. But since
// we're just passing this on to another server, we need to
// re-encode it.
searchString = URLEncoder.encode(searchString);
String numResults = request.getParameter("numResults");
if ((numResults == null) ||
(numResults.equals("0")) ||
(numResults.length() == 0)) {
numResults = "10";
}
String searchEngine =
request.getParameter("searchEngine");
if (searchEngine == null) {
reportProblem(response, "Missing search engine name.");
return;
}
SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
for(int i=0; i<commonSpecs.length; i++) {
SearchSpec searchSpec = commonSpecs[i];
if (searchSpec.getName().equals(searchEngine)) {
String url =
searchSpec.makeURL(searchString, numResults);
response.sendRedirect(url);
return;
}
}
reportProblem(response, "Unrecognized search engine.");
}

private void reportProblem(HttpServletResponse response,
String message)
throws IOException {
response.sendError(response.SC_NOT_FOUND,
"<H2>" + message + "</H2>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


jsp源码实例1(输出)

发表人:lewang | 发表时间: 2006年五月13日, 13:17

package coreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet that generates HTML.
* <P>
* Taken from Core Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.coreservlets.com/.
* &copy; 2000 Marty Hall; may be freely used or adapted.
*/

public class HelloWWW2 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 " +
"Transitional//EN">n";
out.println(docType +
"<HTML>n" +
"<HEAD><TITLE>Hello WWW</TITLE></HEAD>n" +
"<BODY>n" +
"<H1>Hello WWW</H1>n" +
"</BODY></HTML>");
}
}


JSP语法(13)——jspuseBean

发表人:lewang | 发表时间: 2006年五月13日, 13:17

<jsp:useBean>

创建一个Bean实例并指定它的名字和作用范围.


JSP 语法

<jsp:useBean

id="beanInstanceName"

scope="page | request | session | application"

{

class="package.class" |

type="package.class" |

class="package.class" type="package.class" |

beanName="{package.class | <%= expression %>}" type="package.class"

}

{

/> |

> other elements </jsp:useBean>

}

例子

<jsp:useBean id="cart" scope="session" class="session.Carts" />

<jsp:setProperty name="cart" property="*" />


<jsp:useBean id="checking" scope="session" class="bank.Checking" >

<jsp:setProperty name="checking" property="balance" value="0.0" />

</jsp:useBean>


描述

<jsp:useBean>用于定位或示例一个JavaBeans组件。<jsp:useBean>首先会试图定位一个Bean实例,如果这个Bean不存在,那么<jsp:useBean>就会从一个class或模版中进行示例。

为了定位或示例一个Bean,<jsp:useBean>会进行以下步聚,顺序如下:

通过给定名字和范围试图定位一个Bean.
对这个Bean对象引用变量以你指定的名字命名.
如果发现了这个Bean,将会在这个变量中储存这个引用。如果你也指定了类型,那么这个Bean也设置为相应的类型.
如果没有发现这个Bean,将会从你指定的class中示例,并将此引用储存到一个新的变量中去。如果这个class的名字代表的是一个模版,那么这个Bean被java.beans.Beans.instantiate示例.
如果<jsp:useBean>已经示例(不是定位)了Bean,同时<jsp:useBean>和</jsp:useBean>中有元素,那么将会执行其中的代码.

<jsp:useBean>元素的主体通常包含有<jsp:setProperty>元素,用于设置Bean的属性值。正如上面第五步所说的,<jsp:useBean>的主体仅仅只有在<jsp:useBean>示例Bean时才会被执行,如果这个Bean已经存在,<jsp:useBean>能够定位它,那么主体中的内容将不会起作用


属性以及用法


id="beanInstanceName"
在你所定义的范围中确认Bean的变量,你能在后面的程序中使用此变量名来分辨不同的Bean

这个变量名对大小写敏感,必须符合你所使用的脚本语言的规定,在Java

Programming Language中,这个规定在Java Language

规范已经写明。如果这个Bean已经在别的<jsp:useBean>中创建,那么这个id的值必须与原来的那个id值一致.

scope="page | request | session | application"
Bean存在的范围以及id变量名的有效范围。缺省值是page,以下是详细说明:

page - 你能在包含<jsp:useBean>元素的JSP文件以及此文件中的所有静态包含文件中使用Bean,直到页面执行完毕向客户端发回响应或转到另一个文件为止。
request - 你在任何执行相同请求的Jsp文件中使用Bean,直到页面执行完毕向客户端发回响应或转到另一个文件为止。你能够使用Request对象访问Bean,比如request.getAttribute(beanInstanceName)
session - 从创建Bean开始,你就能在任何使用相同session的Jsp文件中使用Bean.这个Bean存在于整个Session生存周期内,任何在分享此Session的Jsp文件都能使用同一Bean.注意在你创建Bean的Jsp文件中<%
@ page %>指令中必须指定session=true

application - 从创建Bean开始,你就能在任何使用相同application的Jsp文件中使用Bean.这个Bean存在于整个application生存周期内,任何在分享此application的Jsp文件都能使用同一Bean.
class="package.class"
使用new关键字以及class构造器从一个class中示例一个bean.这个class不能是抽象的,必须有一个公用的,没有参数的构造器.这个package的名字区别大小写。

type="package.class"
如果这个Bean已经在指定的范围中存在,那么写这个Bean一个新的数据库类型

。如果你没有使用class或beanName指定type,Bean将不会被示例.package和class的名字,区分大小写.

beanName="{package.class | <%= expression %>}" type="package.class"
使用java.beans.Beans.instantiate方法来从一个class或连续模版中示例一个Bean,同时指定Bean的类型。

beanName可以是package和class也可以是表达式,它的值会传给Beans.instantiate.tupe的值可以和Bean相同。

package 和 class 名字区分大小写.


jsp源码实例2(获取表单参数)

发表人:lewang | 发表时间: 2006年五月13日, 13:17

package coreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Shows all the parameters sent to the servlet via either
* GET or POST. Specially marks parameters that have
* no values or multiple values.
* <P>
* Taken from Core Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.coreservlets.com/.
* &copy; 2000 Marty Hall; may be freely used or adapted.
*/

public class ShowParameters extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Reading All Request Parameters";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR="#FDF5E6">n" +
"<H1 ALIGN=CENTER>" + title + "</H1>n" +
"<TABLE BORDER=1 ALIGN=CENTER>n" +
"<TR BGCOLOR="#FFAD00">n" +
"<TH>Parameter Name<TH>Parameter Value(s)");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = (String)paramNames.nextElement();
out.print("<TR><TD>" + paramName + "n<TD>");
String[] paramValues =
request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() == 0)
out.println("<I>No Value</I>");
else
out.println(paramValue);
} else {
out.println("<UL>");
for(int i=0; i<paramValues.length; i++) {
out.println("<LI>" + paramValues[i]);
}
out.println("</UL>");
}
}
out.println("</TABLE>n</BODY></HTML>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


JSP语法(12)——jspuseBean

发表人:lewang | 发表时间: 2006年五月13日, 13:16

<jsp:useBean>

创建一个Bean实例并指定它的名字和作用范围.


JSP 语法
<jsp:useBean

id="beanInstanceName"

scope="page | request | session | application"

{

class="package.class" |

type="package.class" |

class="package.class" type="package.class" |

beanName="{package.class | <%= expression %>}" type="package.class"

}

{

/> |

> other elements </jsp:useBean>

}

例子

<jsp:useBean id="cart" scope="session" class="session.Carts" />

<jsp:setProperty name="cart" property="*" />

<jsp:useBean id="checking" scope="session" class="bank.Checking" >

<jsp:setProperty name="checking" property="balance" value="0.0" />

</jsp:useBean>


JSP语法(11)——jspplugin

发表人:lewang | 发表时间: 2006年五月13日, 13:16

<jsp:plugin>

执行一个applet或Bean,有可能的话还要下载一个Java插件用于执行它.

JSP 语法
<jsp:plugin


type="bean | applet"


code="classFileName"


codebase="classFileDirectoryName"


[ name="instanceName" ]


[ archive="URIToArchive, ..." ]


[ align="bottom | top | middle | left | right" ]


[ height="displayPixels" ]


[ width="displayPixels" ]


[ hspace="leftRightPixels" ]


[ vspace="topBottomPixels" ]


[ jreversion="JREVersionNumber | 1.1" ]


[ nspluginurl="URLToPlugin" ]


[ iepluginurl="URLToPlugin" ] >


[ <jsp:params>


[ <jsp:param name="parameterName"

value="{parameterValue | <%= expression %>}" /> ]+


</jsp:params> ]


[ <jsp:fallback> text message for user </jsp:fallback> ]


</jsp:plugin>


例子

<jsp:plugin type=applet code="Molecule.class" codebase="/html">

<jsp:params>

<jsp:param name="molecule" value="molecules/benzene.mol" />

</jsp:params>

<jsp:fallback>

<p>Unable to load applet</p>

</jsp:fallback>

</jsp:plugin>


描述

<jsp:plugin>元素用于在浏览器中播放或显示一个对象(典型的就是applet和Bean),而这种显示需要在浏览器的java插件。

当Jsp文件被编译,送往浏览器时,<jsp:plugin>元素将会根据浏览器的版本替换成<object>或者<embed>元素。注意,<object>用于HTML 4.0 ,<embed>用于HTML 3.2.

一般来说,<jsp:plugin>元素会指定对象是Applet还是Bean,同样也会指定class的名字,还有位置,另外还会指定将从哪里下载这个Java插件。具体如下:


属性


type="bean | applet"
.将被执行的插件对象的类型,你必须得指定这个是Bean还是applet,因为这个属性没有缺省值.

code="classFileName"
将会被Java插件执行的Java Class的名字,必须以.class结尾。这个文件必须存在于codebase属性指定的目录中.

codebase="classFileDirectoryName"
将会被执行的Java Class文件的目录(或者是路径),如果你没有提供此属性,那么使用<jsp:plugin>的jsp文件的目录将会被使用.

name="instanceName"
这个Bean或applet实例的名字,它将会在Jsp其它的地方调用.

archive="URIToArchive, ..."
一些由逗号分开的路径名,这些路径名用于预装一些将要使用的class,这会提高applet的性能.

align="bottom | top | middle | left | right"
图形,对象,Applet的位置,有以下值:

bottom
top
middle
left
right
height="displayPixels" width="displayPixels"
Applet或Bean将要显示的长宽的值,此值为数字,单位为象素.

hspace="leftRightPixels" vspace="topBottomPixels"
Applet或Bean显示时在屏幕左右,上下所需留下的空间,单位为象素.

jreversion="JREVersionNumber | 1.1"

Applet或Bean运行所需的Java Runtime Environment (JRE) 的版本.

缺省值是 1.1.

nspluginurl="URLToPlugin"

Netscape Navigator用户能够使用的JRE的下载地址,此值为一个标准的URL,如http://www.aspcn.com/jsp

iepluginurl="URLToPlugin"
IE用户能够使用的JRE的下载地址,此值为一个标准的URL,如http://www.aspcn.com/jsp


<jsp:params> [ <jsp:param name="parameterName"
value="{parameterValue | <%= expression %>}" /> ]+ </jsp:params>

你需要向applet或Bean传送的参数或参数值。

<jsp:fallback> text message for user </jsp:fallback>
一段文字用于Java

插件不能启动时显示给用户的,如果插件能够启动而applet或Bean不能,那么浏览器会有一个出错信息弹出.


JSP语法(9)——jspgetProperty

发表人:lewang | 发表时间: 2006年五月13日, 13:15

<jsp:getProperty>


获取Bean的属性值,用于显示在页面中

JSP 语法

<jsp:getProperty name="beanInstanceName" property="propertyName" />


例子


<jsp:useBean id="calendar" scope="page" class="employee.Calendar" />

<h2>

Calendar of <jsp:getProperty name="calendar" property="username" />

</h2>


描述


这个<jsp:getProperty>元素将获得Bean的属性值,并可以将其使用或显示在JSP页面中.在你使用<jsp:getProperty>之前,你必须用<jsp:useBean>创建它.


<jsp:getProperty>元素有一些限制:


你不能使用<jsp:getProperty>来检索一个已经被索引了的属性


你能够和JavaBeans组件一起使用<jsp:getProperty>,但是不能与Enterprise

Bean一起使用。

属性

name="beanInstanceName"
bean的名字,由<jsp:useBean>指定

property="propertyName"
所指定的Bean的属性名。

技巧:


在sun的JSP参考中提到,如果你使用<jsp:getProperty>来检索的值是空值,那么NullPointerException将会出现,同时如果使用程序段或表达式来检索其值,那么在浏览器上出现的是null(空).


JSP语法(8)——jspforward

发表人:lewang | 发表时间: 2006年五月13日, 13:15

<jsp:forward>


重定向一个HTML文件,JSP文件,或者是一个程序段.


JSP 语法


<jsp:forward page={"relativeURL" | "<%= expression %>"} />


or


<jsp:forward page={"relativeURL" | "<%= expression %>"} >


<jsp:param name="parameterName"


value="{parameterValue | <%= expression %>}" />+


</jsp:forward>


例子

<jsp:forward page="/servlet/login" />


<jsp:forward page="/servlet/login">


<jsp:param name="username" value="jsmith" />


</jsp:forward>

描述


<jsp:forward>标签从一个JSP文件向另一个文件传递一个包含用户请求的request对象.<jsp:forward>标签以下的代码,将不能执行.


你能够向目标文件传送参数和值,在这个例子中我们传递的参数名为username,值为scott,如果你使用了<jsp:param>标签的话,目标文件必须是一个动态的文件,能够处理参数.


如果你使用了非缓冲输出的话,那么使用<jsp:forward>时就要小心。如果在你使用<jsp:forward>之前,jsp文件已经有了数据,那么文件执行就会出错.

属性

page="{relativeURL | <%= expression %>}"
这里是一个表达式或是一个字符串用于说明你将要定向的文件或URL.这个文件可以是JSP,程序段,或者其它能够处理request对象的文件(如asp,cgi,php).


<jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" />+
向一个动态文件发送一个或多个参数,这个文件一定是动态文件.


如果你想传递多个参数,你可以在一个JSP文件中使用多个<jsp:param>。name指定参数名,value指定参数值.


JSP语法(10)——jspinclude

发表人:lewang | 发表时间: 2006年五月13日, 13:15

<jsp:include>


包含一个静态或动态文件.


JSP 语法


<jsp:include page="{relativeURL | <%= expression%>}" flush="true" />

or

<jsp:include page="{relativeURL | <%= expression %>}" flush="true" >

<jsp:param name="parameterName"

value="{parameterValue | <%= expression %>}" />+

</jsp:include>

Examples


<jsp:include page="scripts/login.jsp" />

<jsp:include page="copyright.html" />

<jsp:include page="/index.html" />

<jsp:include page="scripts/login.jsp">

<jsp:param name="username" value="jsmith" />

</jsp:include>

描述


<jsp:include>元素允许你包含动态文件和静态,这两种包含文件的结果是不同的。如果文件仅是静态文件,那么这种包含仅仅是把包含文件的内容加到jsp文件中去,而如果这个文件动态的,那么这个被包含文件也会被Jsp编译器执行(这一切与asp相似)


你不能从文件名上判断一个文件是动态的还是静态的,比如aspcn.asp

就有可能只是包含一些信息而已,而不需要执行。<jsp:include>能够同时处理这两种文件,因此你就不需要包含时还要判断此文件是动态的还是静态的.


如果这个包含文件是动态的,那么你还可以用<jsp:param>还传递参数名和参数值。


属性


page="{relativeURL | <%= expression %>}"
参数为一相对路径,或者是代表相对路径的表达式.

flush="true"
这里你必须使用flush="true",你不能使用false值.缺省值为false

<jsp:param name="parameterName" value="{parameterValue | <%= expression %> }" />+
<jsp:param>子句能让你传递一个或多个参数给动态文件


你能在一个页面中使用多个<jsp:param>来传递多个参数。


JSP语法(6)——Page指令

发表人:lewang | 发表时间: 2006年五月13日, 13:14

Page 指令

定义JSP文件中的全局属性.


JSP 语法
<%@ page
[ language="java" ]
[ extends="package.class" ]
[ import="{package.class | package.*}, ..." ]
[ session="true | false" ]
[ buffer="none | 8kb | sizekb" ]
[ autoFlush="true | false" ]
[ isThreadSafe="true | false" ]
[ info="text" ]
[ errorPage="relativeURL" ]
[ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ]
[ isErrorPage="true | false" ]
%>
例子
<%@ page import="java.util.*, java.lang.*" %>
<%@ page buffer="5kb" autoFlush="false" %>
<%@ page errorPage="error.jsp" %>
描述
<%@ page %>指令作用于整个JSP页面,同样包括静态的包含文件。但是<% @ page %>指令不能作用于动态的包含文件,比如 <jsp:include>

你可以在一个页面中用上多个<% @ page %>指令,但是其中的属性只能用一次,不过也有个例外,那就是import属性。因为import属性和Java中的import语句差不多(参照Java Language),所以你就能多用此属性几次了.

无论你把<% @ page %>指令放在JSP的文件的哪个地方,它的作用范围都是整个JSP页面。不过,为了JSP程序的可读性,以及好的编程习惯,最好还是把它放在JSP文件的顶部.


JSP语法(5)——Scriptlet

发表人:lewang | 发表时间: 2006年五月13日, 13:14

Scriptlet

包含一个有效的程序段.

JSP 语法

<% code fragment %>

例子

<%
String name = null;
if (request.getParameter("name") == null) {
%>
<%@ include file="error.html" %>
<%
} else {
foo.setName(request.getParameter("name"));
if (foo.getName().equalsIgnoreCase("integra"))
name = "acura";
if (name.equalsIgnoreCase( "acura" )) {
%>

描述

一个scriptlet能够包含多个jsp语句,方法,变量,表达式

因为scriptlet,我们便能做以下的事:

声明将要用到的变量或方法(参考 声明).
编写JSP表达式(参考 表达式).
使用任何隐含的对象和任何用<jsp:useBean>声明过的对象
编写JSP语句 (如果你在使用Java语言,这些语句必须遵从Java Language Specification,).
任何文本,HTML标记,JSP元素必须在scriptlet之外
当JSP收到客户的请求时,scriptlet就会被执行,如果scriptlet有显示的内容,这些显示的内容就被存在out对象中。


JSP语法(7)——Taglib指令

发表人:lewang | 发表时间: 2006年五月13日, 13:14

Taglib 指令


定义一个标签库以及其自定义标签的前缀.

JSP 语法

<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>

例子

<%@ taglib uri="http://www.jspcentral.com/tags" prefix="public" %>
<public:loop>
.
.
</public:loop>

描述

<% @ taglib %>指令声明此JSP文件使用了自定义的标签,同时引用标签库,也指定了他们的标签的前缀。

这里自定义的标签含有标签和元素之分。因为JSP文件能够转化为XML,所以了解标签和元素之间的联系很重要。标签只不过是一个在意义上被抬高了点的标记,是JSP元素的一部分。JSP元素是JSP语法的一部分,和XML一样有开始标记和结束标记。元素也可以包含其它的文本,标记,元素。比如,一个jsp:plugin元素有<jsp:plugin>开始标记和</jsp:plugin>结束标记,同样也可以有<jsp:params>和<jsp:fallback>元素.

你必须在使用自定义标签之前使用<% @ taglib %>指令,而且你可以在一个页面中多次使用,但是前缀只能使用一次

属性

uri="URIToTagLibrary"
Uniform Resource Identifier (URI)根据标签的前缀对自定义的标签进行唯一的命名,URI可以是以下的内容:

Uniform Resource Locator (URL), 由 RFC 2396 定义, 查看 http://www.hut.fi/u/jkorpela/rfc/2396/full.html
 
Uniform Resource Name (URN), 由 RFC 2396定义
 
一个相对或绝对的路径
prefix="tagPrefix"
在自定义标签之前的前缀,比如,在<public:loop>中的public,如果这里不写public,那么这就是不合法的。请不要用jsp, jspx, java, javax, servlet, sun, 和sunw做为你的前缀


JSP语法(4)——表达式

发表人:lewang | 发表时间: 2006年五月13日, 13:13

表达式

包含一个符合JSP语法的表达式

JSP 语法

<%= expression %>

例子

<font color="blue"><%= map.size() %></font>
<b><%= numguess.getHint() %></b>.

描述

表达式元素表示的是一个在脚本语言中被定义的表达式,在运行后被自动转化为字符串,然后插入到这个表达示在JSP文件的位置显示。因为这个表达式的值已经被转化为字符串,所以你能在一行文本中插入这个表达式(形式和ASP完全一样).

当你在JSP中使用表达式时请记住以下几点:

你不能用一个分号(";")来作为表达式的结束符.但是同样的表达式用在scriptlet中就需要以分号来结尾了!查看Scriptlet 这个表达式元素能够包括任何在Java Language Specification中有效的表达式.

有时候表达式也能做为其它JSP元素的属性值.一个表达式能够变得很复杂,它可能由一个或多个表达式组成,这些表达式的顺序是从左到右。


JSP语法(3)——声明

发表人:lewang | 发表时间: 2006年五月13日, 13:13

声明

在JSP程序中声明合法的变量和方法

JSP 语法

<%! declaration; [ declaration; ]+ ... %>

例子

<%! int i = 0; %>
<%! int a, b, c; %>
<%! Circle a = new Circle(2.0); %>

描述

声明你将要在JSP程序中用到的变量和方法。你也必须这样做,不然会出错.

你可以一次性声明多个变量和方法,只要以";"结尾就行,当然这些声明在Java中要是合法的。

当你声明方法或变量时,请注意以下的一些规则:

声明必须以";"结尾(Scriptlet有同样的规则,但是 表达式就不同了).
你可以直接使用在<% @ page %>中被包含进来的已经声明的变量和方法,不需要对它们重新进行声明.

一个声明仅在一个页面中有效。如果你想每个页面都用到一些声明,最好把它们写成一个单独的文件,然后用<%@ include %>或<jsp:include >元素包含进来。


JSP语法(2)——隐藏注释

发表人:lewang | 发表时间: 2006年五月13日, 13:13

隐藏注释


写在JSP程序中,但不是发给客户。

JSP 语法

<%-- comment --%>

例子:

<%@ page language="java" %>
<html>
<head><title>A Comment Test</title></head>
<body>
<h2>A Test of Comments</h2>
<%-- This comment will not be visible in the page source --%>
</body>
</html>

描述

用隐藏注释标记的字符会在JSP编译时被忽略掉。这个注释在你希望隐藏或注释你的JSP程序时是很有用的。

JSP编译器不是会对<%--and--%>之间的语句进行编译的,它不会显示在客户的浏览器中,也不会在源代码中看到在<%-- --%>之间,你可以任意写注释语句,但是不能使用 "--%>",如果你非要使用请用"--%>".


JSP语法(1)——HTML注释

发表人:lewang | 发表时间: 2006年五月13日, 13:12

HTML 注释

在客户端显示一个注释.

JSP 语法

<!-- comment [ <%= expression %> ] -->

例子 1

<!-- This file displays the user login screen -->

在客户端的HTML源代码中产生和上面一样的数据:

<!-- This file displays the user login screen -->

例子 2

<!-- This page was loaded on <%= (new java.util.Date()).toLocaleString() %> -->

在客户端的HTML源代码中显示为:

<!-- This page was loaded on January 1, 2000 -->

描述

这种注释和HTML中很像,也就是它可以在"查看源代码"中看到。

唯一有些不同的就是,你可以在这个注释中用表达式(例子2所示)。这个表达示是不定的,由页面不同而不同,你能够使用各种表达式,只要是合法的就行。


JDBCTM 指南:入门7 - CallableStatement

发表人:lewang | 发表时间: 2006年五月13日, 13:11

7 - CallableStatement
本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程,同时也是 JDBC 的重要参考手册,它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。


7.1 概述
CallableStatement 对象为所有的 DBMS 提供了一种以标准形式调用已储存过程的方法。已储存过程储存在数据库中。对已储存过程的调用是 CallableStatement 对象所含的内容。这种调用是用一种换码语法来写的,有两种形式:一种形式带结果参数,另一种形式不带结果参数(有关换码语法的信息,参见第 4 节“语句”)。结果参数是一种输出 (OUT) 参数,是已储存过程的返回值。两种形式都可带有数量可变的输入(IN 参数)、输出(OUT 参数)或输入和输出(INOUT 参数)的参数。问号将用作参数的占位符。

在 JDBC 中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。

{call 过程名[(?, ?, ...)]}

返回结果参数的过程的语法为:

{? = call 过程名[(?, ?, ...)]}

不带参数的已储存过程的语法类似:

{call 过程名}

通常,创建 CallableStatement 对象的人应当知道所用的 DBMS 是支持已储存过程的,并且知道这些过程都是些什么。然而,如果需要检查,多种 DatabaseMetaData 方法都可以提供这样的信息。例如,如果 DBMS 支持已储存过程的调用,则 supportsStoredProcedures 方法将返回 true,而 getProcedures 方法将返回对已储存过程的描述。

CallableStatement 继承 Statement 的方法(它们用于处理一般的 SQL 语句),还继承了 PreparedStatement 的方法(它们用于处理 IN 参数)。CallableStatement 中定义的所有方法都用于处理 OUT 参数或 INOUT 参数的输出部分:注册 OUT 参数的 JDBC 类型(一般 SQL 类型)、从这些参数中检索结果,或者检查所返回的值是否为 JDBC NULL。


7.1.1 创建 CallableStatement 对象
CallableStatement 对象是用 Connection 方法 prepareCall 创建的。下例创建 CallableStatement 的实例,其中含有对已储存过程 getTestData 调用。该过程有两个变量,但不含结果参数:

CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");

其中 ? 占位符为 IN、 OUT 还是 INOUT 参数,取决于已储存过程 getTestData。


7.1.2 IN 和 OUT 参数
将 IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成的。该方法继承自 PreparedStatement。所传入参数的类型决定了所用的 setXXX 方法(例如,用 setFloat 来传入 float 值等)。

如果已储存过程返回 OUT 参数,则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数的 JDBC 类型(这是必需的,因为某些 DBMS 要求 JDBC 类型)。注册 JDBC 类型是用 registerOutParameter 方法来完成的。语句执行完后,CallableStatement 的 getXXX 方法将取回参数值。正确的 getXXX 方法是为各参数所注册的 JDBC 类型所对应的 Java 类型(从 JDBC 类型到 Java 类型的标准映射见 8.6.1 节中的表)。换言之, registerOutParameter 使用的是 JDBC 类型(因此它与数据库返回的 JDBC 类型匹配),而 getXXX 将之转换为 Java 类型。

作为示例,下述代码先注册 OUT 参数,执行由 cstmt 所调用的已储存过程,然后检索在 OUT 参数中返回的值。方法 getByte 从第一个 OUT 参数中取出一个 Java 字节,而 getBigDecimal 从第二个 OUT 参数中取出一个 BigDecimal 对象(小数点后面带三位数):

CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
cstmt.executeQuery();
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);

CallableStatement 与 ResultSet 不同,它不提供用增量方式检索大 OUT 值的特殊机制。


7.1.3 INOUT 参数
既支持输入又接受输出的参数(INOUT 参数)除了调用 registerOutParameter 方法外,还要求调用适当的 setXXX 方法(该方法是从 PreparedStatement 继承来的)。setXXX 方法将参数值设置为输入参数,而 registerOutParameter 方法将它的 JDBC 类型注册为输出参数。setXXX 方法提供一个 Java 值,而驱动程序先把这个值转换为 JDBC 值,然后将它送到数据库中。

这种 IN 值的 JDBC 类型和提供给 registerOutParameter 方法的 JDBC 类型应该相同。然后,要检索输出值,就要用对应的 getXXX 方法。例如,Java 类型为 byte 的参数应该使用方法 setByte 来赋输入值。应该给 registerOutParameter 提供类型为 TINYINT 的 JDBC 类型,同时应使用 getByte 来检索输出值 (第 8 节“JDBC 和 Java 类型之间的映射”将给出详细信息和类型映射表)。

下例假设有一个已储存过程 reviseTotal,其唯一参数是 INOUT 参数。方法 setByte 把此参数设为 25,驱动程序将把它作为 JDBC TINYINT 类型送到数据库中。接着,registerOutParameter 将该参数注册为 JDBC TINYINT。执行完该已储存过程后,将返回一个新的 JDBC TINYINT 值。方法 getByte 将把这个新值作为 Java byte 类型检索。

CallableStatement cstmt = con.prepareCall(
"{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);

7.1.4 先检索结果,再检索 OUT 参数
由于某些 DBMS 的限制,为了实现最大的可移植性,建议先检索由执行 CallableStatement 对象所产生的结果,然后再用 CallableStatement.getXXX 方法来检索 OUT 参数。

如果 CallableStatement 对象返回多个 ResultSet 对象(通过调用 execute 方法),在检索 OUT 参数前应先检索所有的结果。这种情况下,为确保对所有的结果都进行了访问,必须对 Statement 方法 getResultSet、getUpdateCount 和 getMoreResults 进行调用,直到不再有结果为止。

检索完所有的结果后,就可用 CallableStatement.getXXX 方法来检索 OUT 参数中的值。


7.1.5 检索作为 OUT 参数的 NULL 值
返回到 OUT 参数中的值可能会是 JDBC NULL。当出现这种情形时,将对 JDBC NULL 值进行转换以使 getXXX 方法所返回的值为 null、0 或 false,这取决于 getXXX 方法类型。对于 ResultSet 对象,要知道 0 或 false 是否源于 JDBC NULL 的唯一方法,是用方法 wasNull 进行检测。如果 getXXX 方法读取的最后一个值是 JDBC NULL,则该方法返回 true,否则返回 flase。第 5 节“ResultSet”将给出详细信息。


JDBCTM 指南:入门6 - PreparedStatement

发表人:lewang | 发表时间: 2006年五月13日, 13:11

6 - PreparedStatement
本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程,同时也是 JDBC 的重要参考手册,它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。


6.1 概述
该 PreparedStatement 接口继承 Statement,并与之在两方面有所不同:


PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。
包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN 参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的 setXXX 方法来提供。

由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值。同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式(接受 SQL 语句参数的形式)不应该用于 PreparedStatement 对象。


6.1.1 创建 PreparedStatement 对象
以下的代码段(其中 con 是 Connection 对象)创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象:

PreparedStatement pstmt = con.prepareStatement(
"UPDATE table4 SET m = ? WHERE x = ?");

pstmt 对象包含语句 "UPDATE table4 SET m = ? WHERE x = ?",它已发送给 DBMS,并为执行作好了准备。


6.1.2 传递 IN 参数
在执行 PreparedStatement 对象之前,必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成,其中 XXX 是与该参数相应的类型。例如,如果参数具有 Java 类型 long,则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为 123456789,第二个参数设为 100000000:

pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);

一旦设置了给定语句的参数值,就可用它多次执行该语句,直到调用 clearParameters 方法清除它为止。

在连接的缺省模式下(启用自动提交),当语句完成时将自动提交或还原该语句。

如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态,则同一个 PreparedStatement 可执行多次。如果这一点不成立,那么试图通过使用 PreparedStatement 对象代替 Statement 对象来提高性能是没有意义的。

利用 pstmt(前面创建的 PreparedStatement 对象),以下代码例示了如何设置两个参数占位符的值并执行 pstmt 10 次。如上所述,为做到这一点,数据库不能关闭 pstmt。在该示例中,第一个参数被设置为 "Hi"并保持为常数。在 for 循环中,每次都将第二个参数设置为不同的值:从 0 开始,到 9 结束。

pstmt.setString(1, "Hi");
for (int i = 0; i < 10; i++) {
pstmt.setInt(2, i);
int rowCount = pstmt.executeUpdate();
}

6.1.3 IN 参数中数据类型的一致性
setXXX 方法中的 XXX 是 Java 类型。它是一种隐含的 JDBC 类型(一般 SQL 类型),因为驱动程序将把 Java 类型映射为相应的 JDBC 类型(遵循该 JDBC Guide中§8.6.2 “映射 Java 和 JDBC 类型”表中所指定的映射),并将该 JDBC 类型发送给数据库。例如,以下代码段将 PreparedStatement 对象 pstmt 的第二个参数设置为 44,Java 类型为 short:

pstmt.setShort(2, 44);

驱动程序将 44 作为 JDBC SMALLINT 发送给数据库,它是 Java short 类型的标准映射。

程序员的责任是确保将每个 IN 参数的 Java 类型映射为与数据库所需的 JDBC 数据类型兼容的 JDBC 类型。不妨考虑数据库需要 JDBC SMALLINT 的情况。如果使用方法 setByte ,则驱动程序将 JDBC TINYINT 发送给数据库。这是可行的,因为许多数据库可从一种相关的类型转换为另一种类型,并且通常 TINYINT 可用于 SMALLINT 适用的任何地方。然而,对于要适用于尽可能多的数据库的应用程序,最好使用与数据库所需的确切的 JDBC 类型相应的 Java 类型。如果所需的 JDBC 类型是 SMALLINT,则使用 setShort 代替 setByte 将使应用程序的可移植性更好。


6.1.4 使用 setObject
程序员可使用 setObject 方法显式地将输入参数转换为特定的 JDBC 类型。该方法可以接受第三个参数,用来指定目标 JDBC 类型。将 Java Object 发送给数据库之前,驱动程序将把它转换为指定的 JDBC 类型。

如果没有指定 JDBC 类型,驱动程序就会将 Java Object 映射到其缺省的 JDBC 类型(参见第 8.6.4 节中的表格),然后将它发送到数据库。这与常规的 setXXX 方法类似;在这两种情况下,驱动程序在将值发送到数据库之前,会将该值的 Java 类型映射为适当的 JDBC 类型。二者的差别在于 setXXX 方法使用从 Java 类型到 JDBC 类型的标准映射(参见第 8.6.2 节中的表格),而 setObject 方法使用从 Java Object 类型到 JDBC 类型的映射(参见第 8.6.4 节中的表格)。

方法 setObject 允许接受所有 Java 对象的能力使应用程序更为通用,并可在运行时接受参数的输入。这种情况下,应用程序在编译时并不清楚输入类型。通过使用 setObject,应用程序可接受所有 Java 对象类型作为输入,并将其转换为数据库所需的 JDBC 类型。第 8.6.5 节中的表格显示了 setObject 可执行的所有可能转换。


6.1.5 将 JDBC NULL 作为 IN 参数发送
setNull 方法允许程序员将 JDBC NULL 值作为 IN 参数发送给数据库。但要注意,仍然必须指定参数的 JDBC 类型。

当把 Java null 值传递给 setXXX 方法时(如果它接受 Java 对象作为参数),也将同样把 JDBC NULL 发送到数据库。但仅当指定 JDBC 类型时,方法 setObject 才能接受 null 值。


6.1.6 发送大的 IN 参数
setBytes 和 setString 方法能够发送无限量的数据。但是,有时程序员更喜欢用较小的块传递大型的数据。这可通过将 IN 参数设置为 Java 输入流来完成。当语句执行时,JDBC 驱动程序将重复调用该输入流,读取其内容并将它们当作实际参数数据传输。

JDBC 提供了三种将 IN 参数设置为输入流的方法:setBinaryStream 用于含有未说明字节的流, setAsciiStream 用于含有 ASCII 字符的流,而 setUnicodeStream 用于含有 Unicode 字符的流。因为必须指定流的总长度,所以这些方法所采用的参数比其它的 setXXX 方法要多一个。这很有必要,因为一些数据库在发送数据之前需要知道其总的传送大小。

以下代码例示了使用流作为 IN 参数来发送文件内容:

java.io.File file = new java.io.File("/tmp/data");
int fileLength = file.length();
java.io.InputStream fin = new java.io.FileInputStream(file);
java.sql.PreparedStatement pstmt = con.prepareStatement(
"UPDATE Table5 SET stuff = ? WHERE index = 4");
pstmt.setBinaryStream (1, fin, fileLength);
pstmt.executeUpdate();

当语句执行时,将反复调用输入流 fin 以传递其数据。


JDBCTM 指南:入门5 - ResultSet

发表人:lewang | 发表时间: 2006年五月13日, 13:10

5 - ResultSet
本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程,同时也是 JDBC 的重要参考手册,它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。


5.1 概述
ResultSet 包含符合 SQL 语句中条件的所有行,并且它通过一套 get 方法(这些 get 方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next 方法用于移动到 ResultSet 中的下一行,使下一行成为当前行。

结果集一般是一个表,其中有查询所返回的列标题及相应的值。例如,如果查询为 SELECT a, b, c FROM Table1,则结果集将具有如下形式:


a b c
-------- --------- --------
12345 Cupertino CA
83472 Redmond WA
83492 Boston MA


下面的代码段是执行 SQL 语句的示例。该 SQL 语句将返回行集合,其中列 1 为 int,列 2 为 String,而列 3 则为字节数组:


java.sql.Statement stmt = conn.createStatement();
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (r.next())
{
// 打印当前行的值。
int i = r.getInt("a");
String s = r.getString("b");
float f = r.getFloat("c");
System.out.println("ROW = " + i + " " + s + " " + f);
}

5.1.1 行和光标
ResultSet 维护指向其当前数据行的光标。每调用一次 next 方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用 next 将把光标置于第一行上,使它成为当前行。随着每次调用 next 导致光标向下移动一行,按照从上至下的次序获取 ResultSet 行。

在 ResultSet 对象或其父辈 Statement 对象关闭之前,光标一直保持有效。

在 SQL 中,结果表的光标是有名字的。如果数据库允许定位更新或定位删除,则需要将光标的名字作为参数提供给更新或删除命令。可通过调用方法 getCursorName 获得光标名。

注意:不是所有的 DBMS 都支持定位更新和删除。可使用 DatabaseMetaData.supportsPositionedDelete 和 supportsPositionedUpdate 方法来检查特定连接是否支持这些操作。当支持这些操作时,DBMS/驱动程序必须确保适当锁定选定行,以使定位更新不会导致更新异常或其它并发问题。


5.1.2 列
方法 getXXX 提供了获取当前行中某列值的途径。在每一行内,可按任何次序获取列值。但为了保证可移植性,应该从左至右获取列值,并且一次性地读取列值。

列名或列号可用于标识要从中获取数据的列。例如,如果 ResultSet 对象 rs 的第二列名为“title”,并将值存储为字符串,则下列任一代码将获取存储在该列中的值:

String s = rs.getString("title");
String s = rs.getString(2);

注意列是从左至右编号的,并且从列 1 开始。同时,用作 getXXX 方法的输入的列名不区分大小写。

提供使用列名这个选项的目的是为了让在查询中指定列名的用户可使用相同的名字作为 getXXX 方法的参数。另一方面,如果 select 语句未指定列名(例如在“select * from table1”中或列是导出的时),则应该使用列号。这些情况下,用户将无法确切知道列名。

有些情况下,SQL 查询返回的结果集中可能有多个列具有相同的名字。如果列名用作 getXXX 方法的参数,则 getXXX 将返回第一个匹配列名的值。因而,如果多个列具有相同的名字,则需要使用列索引来确保检索了正确的列值。这时,使用列号效率要稍微高一些。

关于 ResultSet 中列的信息,可通过调用方法 ResultSet.getMetaData 得到。返回的 ResultSetMetaData 对象将给出其 ResultSet 对象各列的编号、类型和属性。

如果列名已知,但不知其索引,则可用方法 findColumn 得到其列号。


5.1.3 数据类型和转换
对于 getXXX 方法,JDBC 驱动程序试图将基本数据转换成指定 Java 类型,然后返回适合的 Java 值。例如,如果 getXXX 方法为 getString,而基本数据库中数据类型为 VARCHAR,则 JDBC 驱动程序将把 VARCHAR 转换成 Java String。getString 的返回值将为 Java String 对象。

下表显示了允许用 getXXX 获取的 JDBC 类型及推荐用它获取的 JDBC 类型(通用 SQL 类型)。小写的 x 表示允许 getXXX 方法获取该数据类型;大写的 X 表示对该数据类型推荐使用 getXXX 方法。例如,除了 getBytes 和 getBinaryStream 之外的任何 getXXX 方法都可用来获取 LONGVARCHAR 值,但是推荐根据返回的数据类型使用 getAsciiStream 或 getUnicodeStream 方法。方法 getObject 将任何数据类型返回为 Java Object。当基本数据类型是特定于数据库的抽象类型或当通用应用程序需要接受任何数据类型时,它是非常有用的。

可使用 ResultSet.getXXX 方法获取常见的 JDBC 数据类型。

“x”表示该 getXXX 方法可合法地用于获取给定 JDBC 类型。

“X”表示推荐使用该 getXXX 方法来获取给定 JDBC 类型。

  T
I
N
Y
I
N
T S
M
A
L
L
I
N
T I
N
T
E
G
E
R B
I
G

N
T R
E
A
L F
L
O
A
T D
O
U
B
L
E D
E
C
I
M
A
L N
U
M
E
R
I
C B
I
T C
H
A
R V
A
R
C
H
A
R
L
O
N
G
V
A
R
C
H
A
R B
I
N
A
R
Y V
A
R
B
I
N
A
R
Y L
O
N
G
V
A
R
B
I
N
A
R
Y D
A
T
E T
I
M
E T
I
M
E
S
T
A
M
P
getByte X x x x x x x x x x x x x            
getShort x X x x x x x x x x x x x            
getInt x x X x x x x x x x x x x            
getLong x x x X x x x x x x x x x            
getFloat x x x x X x x x x x x x x            
getDouble x x x x x X X x x x x x x            
getBigDecimal x x x x x x x X X x x x x            
getBoolean x x x x x x x x x X x x x            
getString x x x x x x x x x x X X x x x x x x x
getBytes                           X X x      
getDate                     x x x       X   x
getTime                     x x x         X x
getTimestamp                     x x x       x   X
getAsciiStream                     x x X x x x      
getUnicodeStream                     x x X x x x      
getBinaryStream                           x x X      
getObject x x x x x x x x x x x x x x x x x x x

5.1.4 对非常大的行值使用流
ResultSet 可以获取任意大的 LONGVARBINARY 或 LONGVARCHAR 数据。方法 getBytes 和 getString 将数据返回为大的块(最大为 Statement.getMaxFieldSize 的返回值)。但是,以较小的固定块获取非常大的数据可能会更方便,而这可通过让 ResultSet 类返回 java.io.Input 流来完成。从该流中可分块读取数据。注意:必须立即访问这些流,因为在下一次对 ResultSet 调用 getXXX 时它们将自动关闭(这是由于基本实现对大块数据访问有限制)。

JDBC API 具有三个获取流的方法,分别具有不同的返回值:


getBinaryStream 返回只提供数据库原字节而不进行任何转换的流。


getAsciiStream 返回提供单字节 ASCII 字符的流。


getUnicodeStream 返回提供双字节 Unicode 字符的流。


注意:它不同于 Java 流,后者返回无类型字节并可(例如)通用于 ASCII 和 Unicode 字符。

下列代码演示了 getAsciiStream 的用法:

java.sql.Statement stmt = con.createStatement();
ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
// 现在以 4K 块大小获取列 1 结果:
byte buff = new byte[4096];
while (r.next()) {
Java.io.InputStream fin = r.getAsciiStream(1);
for (;;) {
int size = fin.read(buff);
if (size == -1) { // 到达流末尾
break;
}
// 将新填充的缓冲区发送到 ASCII 输出流:
output.write(buff, 0, size);
}
}

5.1.5 NULL 结果值
要确定给定结果值是否是 JDBC NULL,必须先读取该列,然后使用 ResultSet.wasNull 方法检查该次读取是否返回 JDBC NULL。

当使用 ResultSet.getXXX 方法读取 JDBC NULL 时,方法 wasNull 将返回下列值之一:


Java null 值:对于返回 Java 对象的 getXXX 方法(例如 getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject 等)。


零值:对于 getByte、getShort、getInt、getLong、getFloat 和 getDouble。


false 值:对于 getBoolean。


5.1.6 可选结果集或多结果集
通常使用 executeQuery(它返回单个 ResultSet)或 executeUpdate(它可用于任何数据库修改语句,并返回更新行数)可执行 SQL 语句。但有些情况下,应用程序在执行语句之前不知道该语句是否返回结果集。此外,有些已存储过程可能返回几个不同的结果集和/或更新计数。

为了适应这些情况,JDBC 提供了一种机制,允许应用程序执行语句,然后处理由结果集和更新计数组成的任意集合。这种机制的原理是首先调用一个完全通用的 execute 方法,然后调用另外三个方法,getResultSet、getUpdateCount 和 getMoreResults。这些方法允许应用程序一次一个地研究语句结果,并确定给定结果是 ResultSet 还是更新计数。

用户不必关闭 ResultSet;当产生它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该 ResultSet 将被 Statement 自动关闭。


JDBCTM 指南:入门4 - Statement

发表人:lewang | 发表时间: 2006年五月13日, 13:09

4 - Statement
本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程,同时也是 JDBC 的重要参考手册,它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。


4.1 概述
Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象,它们都作为在给定连接上执行 SQL 语句的包容器:Statement、PreparedStatement(它从 Statement 继承而来)和 CallableStatement(它从 PreparedStatement 继承而来)。它们都专用于发送特定类型的 SQL 语句: Statement 对象用于执行不带参数的简单 SQL 语句;PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句;CallableStatement 对象用于执行对数据库已存储过程的调用。

Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。


4.1.1 创建 Statement 对象
建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:

Connection con = DriverManager.getConnection(url, "sunny", "");
Statement stmt = con.createStatement();

为了执行 Statement 对象,被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法:

ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");

4.1.2 使用 Statement 对象执行语句
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。

方法 executeQuery 用于产生单个结果集的语句,例如 SELECT 语句。

方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。

方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能,所以本概述后面将在单独一节中对其进行介绍。

执行语句的所有方法都将关闭所调用的 Statement 对象的当前打开结果集(如果存在)。这意味着在重新执行 Statement 对象之前,需要完成对当前 ResultSet 对象的处理。

应注意,继承了 Statement 接口中所有方法的 PreparedStatement 接口都有自己的 executeQuery、executeUpdate 和 execute 方法。Statement 对象本身不包含 SQL 语句,因而必须给 Statement.execute 方法提供 SQL 语句作为参数。PreparedStatement 对象并不将 SQL 语句作为参数提供给这些方法,因为它们已经包含预编译 SQL 语句。CallableStatement 对象继承这些方法的 PreparedStatement 形式。对于这些方法的 PreparedStatement 或 CallableStatement 版本,使用查询参数将抛出 SQLException。


4.1.3 语句完成
当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的 executeQuery 方法,在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executeUpdate,当它执行时语句即完成。但在少数调用方法 execute 的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。

有些 DBMS 将已存储过程中的每条语句视为独立的语句;而另外一些则将整个过程视为一个复合语句。在启用自动提交时,这种差别就变得非常重要,因为它影响什么时候调用 commit 方法。在前一种情况中,每条语句单独提交;在后一种情况中,所有语句同时提交。


4.1.4 关闭 Statement 对象
Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源,有助于避免潜在的内存问题。


4.1.5 Statement 对象中的 SQL 转义语法
Statement 可包含使用 SQL 转义语法的 SQL 语句。转义语法告诉驱动程序其中的代码应该以不同方式处理。驱动程序将扫描任何转义语法,并将它转换成特定数据库可理解的代码。这使得转义语法与 DBMS 无关,并允许程序员使用在没有转义语法时不可用的功能。

转义子句由花括号和关键字界定:

{keyword . . . parameters . . . }

该关键字指示转义子句的类型,如下所示。


escape 表示 LIKE 转义字符


字符“%”和“_”类似于 SQL LIKE 子句中的通配符(“%”匹配零个或多个字符,而“_”则匹配一个字符)。为了正确解释它们,应在其前面加上反斜杠(“”),它是字符串中的特殊转义字符。在查询末尾包括如下语法即可指定用作转义字符的字符:

{escape 'escape-character'}


例如,下列查询使用反斜杠字符作为转义字符,查找以下划线开头的标识符名:

stmt.executeQuery("SELECT name FROM Identifiers
WHERE Id LIKE `_%' {escape `'};


fn 表示标量函数


几乎所有 DBMS 都具有标量值的数值、字符串、时间、日期、系统和转换函数。要使用这些函数,可使用如下转义语法:关键字 fn 后跟所需的函数名及其参数。例如,下列代码调用函数 concat 将两个参数连接在一起:

{fn concat("Hot", "Java")};


可用下列语法获得当前数据库用户名:

{fn user()};


标量函数可能由语法稍有不同的 DBMS 支持,而它们可能不被所有驱动程序支持。各种 DatabaseMetaData 方法将列出所支持的函数。例如,方法 getNumericFunctions 返回用逗号分隔的数值函数列表,而方法 getStringFunctions 将返回字符串函数,等等。

驱动程序将转义函数调用映射为相应的语法,或直接实现该函数。


d、t 和 ts 表示日期和时间文字


DBMS 用于日期、时间和时间标记文字的语法各不相同。JDBC 使用转义子句支持这些文字的语法的 ISO 标准格式。驱动程序必须将转义子句转换成 DBMS 表示。

例如,可用下列语法在 JDBC SQL 语句中指定日期:

{d `yyyy-mm-dd'}


在该语法中,yyyy 为年代,mm 为月份,而 dd 则为日期。驱动程序将用等价的特定于 DBMS 的表示替换这个转义子句。例如,如果 '28- FEB-99' 符合基本数据库的格式,则驱动程序将用它替换 {d 1999-02-28}。

对于 TIME 和 TIMESTAMP 也有类似的转义子句:

{t `hh:mm:ss'}
{ts `yyyy-mm-dd hh:mm:ss.f . . .'}


TIMESTAMP 中的小数点后的秒(.f . . .)部分可忽略。


call 或 ? = call 表示已存储过程

如果数据库支持已存储过程,则可从 JDBC 中调用它们,语法为:

{call procedure_name[(?, ?, . . .)]}


或(其中过程返回结果参数):

{? = call procedure_name[(?, ?, . . .)]}


方括号指示其中的内容是可选的。它们不是语法的必要部分。

输入参数可以为文字或参数。有关详细信息,参见 JDBC 指南中第 7 节,“CallableStatement”。

可通过调用方法 DatabaseMetaData.supportsStoredProcedures 检查数据库是否支持已存储过程。

oj 表示外部连接

外部连接的语法为

{oj outer-join}


其中 outer-join 形式为

table LEFT OUTER JOIN {table / outer-join} ON search-condition


外部连接属于高级功能。有关它们的解释可参见 SQL 语法。JDBC 提供了三种 DatabaseMetaData 方法用于确定驱动程序支持哪些外部连接类型:supportsOuterJoins、supportsFullOuterJoins 和 supportsLimitedOuterJoins。


方法 Statement.setEscapeProcessing 可打开或关闭转义处理;缺省状态为打开。当性能极为重要时,程序员可能想关闭它以减少处理时间。但通常它将出于打开状态。应注意: setEscapeProcessing 不适用于 PreparedStatement 对象,因为在调用该语句前它就可能已被发送到数据库。有关预编译的信息,参见 PreparedStatement。


4.1.6 使用方法 execute
execute 方法应该仅在语句能返回多个 ResultSet 对象、多个更新计数或 ResultSet 对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知 SQL 字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的情况,尽管这种情况很少见。例如,用户可能执行一个已存储过程(使用 CallableStatement 对象 - 参见第 135 页的 CallableStatement),并且该已存储过程可执行更新,然后执行选择,再进行更新,再进行选择,等等。通常使用已存储过程的人应知道它所返回的内容。

因为方法 execute 处理非常规情况,所以获取其结果需要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法 execute 执行该过程后,必须调用方法 getResultSet 获得第一个结果集,然后调用适当的 getXXX 方法获取其中的值。要获得第二个结果集,需要先调用 getMoreResults 方法,然后再调用 getResultSet 方法。如果已知某个过程返回两个更新计数,则首先调用方法 getUpdateCount,然后调用 getMoreResults,并再次调用 getUpdateCount。

对于不知道返回内容,则情况更为复杂。如果结果是 ResultSet 对象,则方法 execute 返回 true;如果结果是 Java int,则返回 false。如果返回 int,则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 之后要做的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 可以获得两个或多个 ResultSet 对象中第一个对象;或调用方法 getUpdateCount 可以获得两个或多个更新计数中第一个更新计数的内容。

当 SQL 语句的结果不是结果集时,则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下,判断 null 真正含义的唯一方法是调用方法 getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;如果为 -1 则表示结果是结果集或没有结果。如果方法 getResultSet 已返回 null(表示结果不是 ResultSet 对象),则返回值 -1 表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):

((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))

如果已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象,则有必要调用方法 getMoreResults 以确定是否有其它结果集或更新计数。如果 getMoreResults 返回 true,则需要再次调用 getResultSet 来检索下一个结果集。如上所述,如果 getResultSet 返回 null,则需要调用 getUpdateCount 来检查 null 是表示结果为更新计数还是表示没有其它结果。

当 getMoreResults 返回 false 时,它表示该 SQL 语句返回一个更新计数或没有其它结果。因此需要调用方法 getUpdateCount 来检查它是哪一种情况。在这种情况下,当下列条件为真时表示没有其它结果:

((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))

下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的全部结果集和更新计数:


stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // 它是更新计数
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
continue;
}
if (rowCount == 0) { // DDL 命令或 0 个更新
System.out.println(" No rows changed or statement was DDL
command");
stmt.getMoreResults();
continue;
}

// 执行到这里,证明有一个结果集
// 或没有其它结果

ResultSet rs = stmt.getResultSet;
if (rs != null) {
. . . // 使用元数据获得关于结果集列的信息
while (rs.next()) {
. . . // 处理结果
stmt.getMoreResults();
continue;
}
break; // 没有其它结果


JDBCTM 指南:入门3 - DriverManager

发表人:lewang | 发表时间: 2006年五月13日, 13:09

3 - DriverManager
3.1 概述
DriverManager 类是 JDBC 的管理层,作用于用户和驱动程序之间。
它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。
另外,DriverManager 类也处理诸如驱动程序登录时间限制及登录和
跟踪消息的显示等事务。

对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法
是 DriverManager.getConnection。正如名称所示,该方法将建立与
数据库的连接。JDBC 允许用户调用 DriverManager 的方法
getDriver、getDrivers 和 registerDriver 及 Driver 的方法
connect。但多数情况下,让 DriverManager 类管理建立连接的细
节为上策。


3.1.1 跟踪可用驱动程序
DriverManager 类包含一列 Driver 类,它们已通过调用方法
DriverManager.registerDriver 对自己进行了注册。所有 Driver
类都必须包含有一个静态部分。它创建该类的实例,然后在加载该
实例时 DriverManager 类进行注册。这样,用户正常情况下将不会
直接调用 DriverManager.registerDriver;而是在加载驱动程序时
由驱动程序自动调用。加载 Driver 类,然后自动在 DriverManager
中注册的方式有两种:


通过调用方法 Class.forName。这将显式地加载驱动程序类。由于这
与外部设置无关,因此推荐使用这种加载驱动程序的方法。以下代码
加载类 acme.db.Driver:
Class.forName("acme.db.Driver");

如果将 acme.db.Driver 编写为加载时创建实例,并调用以该实例为
参数的 DriverManager.registerDriver(本该如此),则它在
DriverManager 的驱动程序列表中,并可用于创建连接。


通过将驱动程序添加到 java.lang.System 的属性 jdbc.drivers 中
这是一个由 DriverManager 类加载的驱动程序类名的列表,由冒号
分隔:初始化 DriverManager 类时,它搜索系统属性 jdbc.drivers,
如果用户已输入了一个或多个驱动程序,则 DriverManager 类将试图加载它们。
以下代码说明程序员如何在 ~/.hotjava/properties 中输入三个驱动程序类(
启动时,HotJava 将把它加载到系统属性列表中):
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;

对 DriverManager 方法的第一次调用将自动加载这些驱动程序类。

注意:加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能
保证,则调用方法 Class.forName 显式地加载每个驱动程序就显得更为
安全。这也是引入特定驱动程序的方法,因为一旦 DriverManager 类被初始化,它
将不再检查 jdbc.drivers 属性列表。

在以上两种情况中,新加载的 Driver 类都要通过调用 DriverManager.registerDriver
类进行自我注册。如上所述,加载类时将自动执行这一过程。

由于安全方面的原因,JDBC 管理层将跟踪哪个类加载器提供哪
个驱动程序。这样,当 DriverManager 类打开连接时,它仅使用
本地文件系统或与发出连接请求的代码相同的类加载器提供的驱动程序。


3.1.2 建立连接
加载 Driver 类并在 DriverManager 类中注册后,它们即可用来与数
据库建立连接。当调用 DriverManager.getConnection 方法发出连接
请求时,DriverManager 将检查每个驱动程序,查看它是否可以建立连接。

有时可能有多个 JDBC 驱动程序可以与给定的 URL 连接。例如,与
给定远程数据库连接时,可以使用 JDBC-ODBC 桥驱动程序、JDBC 到
通用网络协议驱动程序或数据库厂商提供的驱动程序。在这种情况下
测试驱动程序的顺序至关重要,因为 DriverManager 将使用它所找到
的第一个可以成功连接到给定 URL 的驱动程序。

首先 DriverManager 试图按注册的顺序使用每个驱动程序
(jdbc.drivers 中列出的驱动程序总是先注册)。它将跳过代码不可
信任的驱动程序,除非加载它们的源与试图打开连接的代码的源相同。

它通过轮流在每个驱动程序上调用方法 Driver.connect,并向它们传
递用户开始传递给方法 DriverManager.getConnection 的 URL 来对驱
动程序进行测试,然后连接第一个认出该 URL 的驱动程序。

这种方法初看起来效率不高,但由于不可能同时加载数十个驱动程序,
因此每次连接实际只需几个过程调用和字符串比较。

以下代码是通常情况下用驱动程序(例如 JDBC-ODBC 桥驱动程序)
建立连接所需所有步骤的示例:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载驱动程序
String url = "jdbc:odbc:fred";
DriverManager.getConnection(url, "userID", "passwd");


JDBCTM 指南:入门2 - 连接

发表人:lewang | 发表时间: 2006年五月13日, 13:08

JDBCTM 指南:入门2 - 连接
  2 - 连接
  本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程,同时也是 JDBC 的重要参考手册,它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。


  2.1 概述
  Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL 语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。


  2.1.1 打开连接
  与数据库建立连接的标准方法是调用 DriverManager.getConnection 方法。该方法接受含有某个 URL 的字符串。DriverManager 类(即所谓的 JDBC 管理层)将尝试找到可与那个 URL 所代表的数据库进行连接的驱动程序。DriverManager 类存有已注册的 Driver 类的清单。当调用方法 getConnection 时,它将检查清单中的每个驱动程序,直到找到可与 URL 中指定的数据库进行连接的驱动程序为止。Driver 的方法 connect 使用这个 URL 来建立实际的连接。

用户可绕过 JDBC 管理层直接调用 Driver 方法。这在以下特殊情况下将很有用:当两个驱动器可同时连接到数据库中,而用户需要明确地选用其中特定的驱动器。但一般情况下,让 DriverManager 类处理打开连接这种事将更为简单。

下述代码显示如何打开一个与位于 URL "jdbc:odbc:wombat" 的数据库的连接。所用的用户标识符为 "oboy" ,口令为 "12Java":

String url = "jdbc:odbc:wombat";
  Connection con = DriverManager.getConnection(url, "oboy", "12Java");

2.1.2 一般用法的 URL
  由于 URL 常引起混淆,我们将先对一般 URL 作简单说明,然后再讨论 JDBC URL。

URL(统一资源定位符)提供在 Internet 上定位资源所需的信息。可将它想象为一个地址。

URL 的第一部份指定了访问信息所用的协议,后面总是跟着冒号。常用的协议有 "ftp"(代表“文件传输协议”)和 "http" (代表“超文本传输协议”)。如果协议是 "file",表示资源是在某个本地文件系统上而非在 Internet 上(下例用于表示我们所描述的部分;它并非 URL 的组成部分)。

ftp://javasoft.com/docs/JDK-1_apidocs.zip
  http://java.sun.com/products/jdk/CurrentRelease
  file:/home/haroldw/docs/books/tutorial/summary.html

URL 的其余部份(冒号后面的)给出了数据资源所处位置的有关信息。如果协议是 file,则 URL 的其余部份是文件的路径。对于 ftp 和 http 协议,URL 的其余部份标识了主机并可选地给出某个更详尽的地址路径。例如,以下是 JavaSoft 主页的 URL。该 URL 只标识了主机:

http://java.sun.com

从该主页开始浏览,就可以进到许多其它的网页中,其中之一就是 JDBC 主页。JDBC 主页的 URL 更为具体,它看起来类似:

http://java.sun.com/products/jdbc

2.1.3 JDBC URL
  JDBC URL 提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之建立连接。实际上,驱动程序编程员将决定用什么 JDBC URL 来标识特定的驱动程序。用户不必关心如何来形成 JDBC URL;他们只须使用与所用的驱动程序一起提供的 URL 即可。JDBC 的作用是提供某些约定,驱动程序编程员在构造他们的 JDBC URL 时应该遵循这些约定。

由于 JDBC URL 要与各种不同的驱动程序一起使用,因此这些约定应非常灵活。首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。例如, odbc 子协议允许(但并不是要求) URL 含有属性值。

第二,JDBC URL 应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与给定数据库对话的 applet 打开数据库连接,而无须要求用户去做任何系统管理工作。

第三, JDBC URL 应允许某种程度的间接性。也就是说,JDBC URL 可指向逻辑主机或数据库名,而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系统管理员不必将特定主机声明为 JDBC 名称的一部份。网络命名服务(例如 DNS、 NIS 和 DCE )有多种,而对于使用哪种命名服务并无限制。

JDBC URL 的标准语法如下所示。它由三部分组成,各部分间用冒号分隔:

jdbc::

JDBC URL 的三个部分可分解如下:


  jdbc ─ 协议。JDBC URL 中的协议总是 jdbc。


   ─ 驱动程序名或数据库连接机制(这种机制可由一个或多个驱动程序支持)的名称。子协议名的典型示例是 "odbc",该名称是为用于指定 ODBC 风格的数据资源名称的 URL 专门保留的。例如,为了通过 JDBC-ODBC 桥来访问某个数据库,可以用如下所示的 URL:
  jdbc:odbc:fred

本例中,子协议为 "odbc",子名称 "fred" 是本地
  ODBC 数据资源。

如果要用网络命名服务(这样 JDBC URL 中的数据库名称不必是实际名称),则命名服务可以作为子协议。例如,可用如下所示的 URL :

jdbc:dcenaming:accounts-payable

本例中,该 URL 指定了本地 DCE 命名服务应该将
  数据库名称 "accounts-payable" 解析为更为具体的
  可用于连接真实数据库的名称。


   ─ 一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称(含有驱动程序编程员所选的任何内部语法)。使用子名称的目的是为定位数据库提供足够的信息。前例中,因为 ODBC 将提供其余部份的信息,因此用 "fred" 就已足够。然而,位于远程服务器上的数据库需要更多的信息。例如,如果数据库是通过 Internet 来访问的,则在 JDBC URL 中应将网络地址作为子名称的一部份包括进去,且必须遵循如下所示的标准 URL 命名约定:
  //主机名:端口/子协议

假设 "dbnet" 是个用于将某个主机连接到 Internet 上的协议,则 JDBC URL 类似:

jdbc:dbnet://wombat:356/fred

2.1.4 "odbc" 子协议
  子协议 odbc 是一种特殊情况。它是为用于指定 ODBC 风格的数据资源名称的 URL 而保留的,并具有下列特性:允许在子名称(数据资源名称)后面指定任意多个属性值。odbc 子协议的完整语法为:


  jdbc:odbc:[;=]*

因此,以下都是合法的 jdbc:odbc 名称:

jdbc:odbc:qeor7
  jdbc:odbc:wombat
  jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER
  jdbc:odbc:qeora;UID=kgh;PWD=fooey

2.1.5 注册子协议
  驱动程序编程员可保留某个名称以将之用作 JDBC URL 的子协议名。当 DriverManager 类将此名称加到已注册的驱动程序清单中时,为之保留该名称的驱动程序应能识别该名称并与它所标识的数据库建立连接。例如,odbc 是为 JDBC- ODBC 桥而保留的。示例之二,假设有个 Miracle 公司,它可能会将 "miracle" 注册为连接到其 Miracle DBMS 上的 JDBC 驱动程序的子协议,从而使其他人都无法使用这个名称。

JavaSoft 目前作为非正式代理负责注册 JDBC 子协议名称。要注册某个子协议名称,请发送电子邮件到下述地址:

jdbc@wombat.eng.sun.com

2.1.6 发送 SQL 语句
  连接一旦建立,就可用来向它所涉及的数据库传送 SQL 语句。JDBC 对可被发送的 SQL 语句类型不加任何限制。这就提供了很大的灵活性,即允许使用特定的数据库语句或甚至于非 SQL 语句。然而,它要求用户自己负责确保所涉及的数据库可以处理所发送的 SQL 语句,否则将自食其果。例如,如果某个应用程序试图向不支持储存程序的 DBMS 发送储存程序调用,就会失败并将抛出异常。JDBC 要求驱动程序应至少能提供 ANSI SQL-2 Entry Level 功能才可算是符合 JDBC 标准TM 的。这意味着用户至少可信赖这一标准级别的功能。

JDBC 提供了三个类,用于向数据库发送 SQL 语句。Connection 接口中的三个方法可用于创建这些类的实例。下面列出这些类及其创建方法:


  Statement ─ 由方法 createStatement 所创建。Statement 对象用于发送简单的 SQL 语句。
  PreparedStatement ─ 由方法 prepareStatement 所创建。PreparedStatement 对象用于发送带有一个或多个输入参数( IN 参数)的 SQL 语句。PreparedStatement 拥有一组方法,用于设置 IN 参数的值。执行语句时,这些 IN 参数将被送到数据库中。PreparedStatement 的实例扩展了 Statement ,因此它们都包括了 Statement 的方法。PreparedStatement 对象有可能比 Statement 对象的效率更高,因为它已被预编译过并存放在那以供将来使用。
  CallableStatement ─ 由方法 prepareCall 所创建。CallableStatement 对象用于执行 SQL 储存程序 ─ 一组可通过名称来调用(就象函数的调用那样)的 SQL 语句。CallableStatement 对象从 PreparedStatement 中继承了用于处理 IN 参数的方法,而且还增加了用于处理 OUT 参数和 INOUT 参数的方法。

以下所列提供的方法可以快速决定应用哪个 Connection 方法来创建不同类型的 SQL 语句:


  createStatement 方法用于:


  简单的 SQL 语句(不带参数)


  prepareStatement 方法用于:


  带一个或多个 IN 参数的 SQL 语句


  经常被执行的简单 SQL 语句


  prepareCall 方法用于:


  调用已储存过程


  2.1.7 事务
  事务由一个或多个这样的语句组成:这些语句已被执行、完成并被提交或还原。当调用方法 commit 或 rollback 时,当前事务即告就结束,另一个事务随即开始。

缺省情况下,新连接将处于自动提交模式。也就是说,当执行完语句后,将自动对那个语句调用 commit 方法。这种情况下,由于每个语句都是被单独提交的,因此一个事务只由一个语句组成。如果禁用自动提交模式,事务将要等到 commit 或 rollback 方法被显式调用时才结束,因此它将包括上一次调用 commit 或 rollback 方法以来所有执行过的语句。对于第二种情况,事务中的所有语句将作为组来提交或还原。

方法 commit 使 SQL 语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的全部锁。而方法 rollback 将弃去那些更改。

有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组合在一个事务中来达到。如果两个更新都是成功的,则调用 commit 方法,从而使两个更新结果成为永久性的;如果其中之一或两个更新都失败了,则调用 rollback 方法,以将值恢复为进行更新之前的值。

大多数 JDBC 驱动程序都支持事务。事实上,符合 JDBC 的驱动程序必须支持事务。DatabaseMetaData 给出的信息描述 DBMS 所提供的事务支持水平。


  2.1.8 事务隔离级别
  如果 DBMS 支持事务处理,它必须有某种途径来管理两个事务同时对一个数据库进行操作时可能发生的冲突。用户可指定事务隔离级别,以指明 DBMS 应该花多大精力来解决潜在冲突。例如,当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时该怎么办? 假设第一个事务被还原后,第二个事务所读取的更改值将是无效的,那么是否可允许这种冲突? JDBC 用户可用以下代码来指示 DBMS 允许在值被提交前读取该值(“dirty 读取”),其中 con 是当前连接:

con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

事务隔离级别越高,为避免冲突所花的精力也就越多。Connection 接口定义了五级,其中最低级别指定了根本就不支持事务,而最高级别则指定当事务在对某个数据库进行操作时,任何其它事务不得对那个事务正在读取的数据进行任何更改。通常,隔离级别越高,应用程序执行的速度也就越慢(由于用于锁定的资源耗费增加了,而用户间的并发操作减少了)。在决定采用什么隔离级别时,开发人员必须在性能需求和数据一致性需求之间进行权衡。当然,实际所能支持的级别取决于所涉及的 DBMS 的功能。

当创建 Connection 对象时,其事务隔离级别取决于驱动程序,但通常是所涉及的数据库的缺省值。用户可通过调用 setIsolationLevel 方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别,必须在该事务开始前进行设置,并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改,因为这将立即触发 commit 方法的调用,使在此之前所作的任何更改变成永久性的。


JDBCTM 指南:入门

发表人:lewang | 发表时间: 2006年五月13日, 13:07

1.1 什么是 JDBCTM?
JDBCTM 是一种用于执行 SQL 语句的 JavaTM API(有意思的是,JDBC 本身是个商标名而不是一个缩写字;然而,JDBC
常被认为是代表 “Java 数据库连接 (Java Database Connectivity)”)。它由一组用 Java 编程语言编写的类和接口
组成。JDBC 为工具/数据库开发人员提供了一个标准的 API,使他们能够用纯 Java API 来编写数据库应用程序。

有了 JDBC,向各种关系数据库发送 SQL 语句就是一件很容易的事。换言之,有了 JDBC API,就不必为访问 Sybase 数
据库专门写一个程序,为访问 Oracle 数据库又专门写一个程序,为访问 Informix 数据库又写另一个程序,等等。您只
需用 JDBC API 写一个程序就够了,它可向相应数据库发送 SQL 语句。而且,使用 Java 编程语言编写的应用程序,就
无须去忧虑要为不同的平台编写不同的应用程序。将 Java 和 JDBC 结合起来将使程序员只须写一遍程序就可让它在任何
平台上运行。

Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库应用程序的杰出语言。所需要的
只是 Java 应用程序与各种不同数据库之间进行对话的方法。而 JDBC 正是作为此种用途的机制。

JDBC 扩展了 Java 的功能。例如,用 Java 和 JDBC API 可以发布含有 applet 的网页,而该 applet 使用的信息可能来
自远程数据库。企业也可以用 JDBC 通过 Intranet 将所有职员连到一个或多个内部数据库中(即使这些职员所用的计算机
有 Windows、 Macintosh 和 UNIX 等各种不同的操作系统)。随着越来越多的程序员开始使用 Java 编程语言,对从 Java
中便捷地访问数据库的要求也在日益增加。

MIS 管理员们都喜欢 Java 和 JDBC 的结合,因为它使信息传播变得容易和经济。企业可继续使用它们安装好的数据库,并
能便捷地存取信息,即使这些信息是储存在不同数据库管理系统上。新程序的开发期很短。安装和版本控制将大为简化。程
序员可只编写一遍应用程序或只更新一次,然后将它放到服务器上,随后任何人就都可得到最新版本的应用程序。对于商务
上的销售信息服务, Java 和 JDBC 可为外部客户提供获取信息更新的更好方法。


1.1.1 JDBC 的用途是什么?
简单地说,JDBC 可做三件事:

与数据库建立连接,
发送 SQL 语句,
处理结果。


下列代码段给出了以上三步的基本示例:

Connection con = DriverManager.getConnection (
"jdbc:odbc:wombat", "login", "password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}


1.1.2 JDBC 是一种低级 API ,是高级 API 的基础
JDBC 是个“低级”接口,也就是说,它用于直接调用 SQL 命令。在这方面它的功能极佳,并比其它的数据库连接 API 易
于使用,但它同时也被设计为一种基础接口,在它之上可以建立高级接口和工具。高级接口是“对用户友好的”接口,它使
用的是一种更易理解和更为方便的 API,这种 API 在幕后被转换为诸如 JDBC 这样的低级接口。在编写本文时,正在开发
两种基于 JDBC 的高级 API:


一种用于 Java 的嵌入式 SQL。至少已经有一个提供者计划编写它。DBMS 实现 SQL:一种专门设计来与数据库联合使用的
语言。JDBC 要求 SQL 语句必须作为 String 传给 Java 方法。相反,嵌入式 SQL 预处理器允许程序员将 SQL 语句直接与
Java 混在一起使用。例如,可在 SQL 语句中使用 Java 变量,用以接受或提供 SQL 值。然后,嵌入式 SQL 预处理器将通
过 JDBC 调用把这种 Java/SQL 的混合物转换为 Java。
关系数据库表到 Java 类的直接映射。JavaSoft 和其它提供者都声称要实现该 API。在这种“对象/关系”映射中,表中的
每行对应于类的一个实例,而每列的值对应于该实例的一个属性。于是,程序员可直接对 Java 对象进行操作;存取数据所
需的 SQL 调用将在“掩盖下”自动生成。此外还可提供更复杂的映射,例如将多个表中的行结合进一个 Java 类中。
随着人们对 JDBC 的兴趣日益增涨,越来越多的开发人员一直在使用基于 JDBC 的工具,以使程序的编写更加容易。程序员
也一直在编写力图使最终用户对数据库的访问变得更为简单的应用程序。例如,应用程序可提供一个选择数据库任务的菜单。
任务被选定后,应用程序将给出提示及空白供填写执行选定任务所需的信息。所需信息输入后,应用程序将自动调用所需的
SQL 命令。在这样一种程序的协助下,即使用户根本不懂 SQL 的语法,也可以执行数据库任务。


1.1.3 JDBC 与 ODBC 和其它 API 的比较
目前,Microsoft 的 ODBC(开放式数据库连接)API 可能是使用最广的、用于访问关系数据库的编程接口。它能在几乎所
有平台上连接几乎所有的数据库。为什么 Java 不使用 ODBC?

对这个问题的回答是:Java 可以使用 ODBC,但最好是在 JDBC 的帮助下以 JDBC-ODBC 桥的形式使用,这一点我们稍后再
说。现在的问题已变成:“为什么需要 JDBC”? 回答如下:


ODBC 不适合直接在 Java 中使用,因为它使用 C 语言接口。从 Java 调用本地 C 代码在安全性、实现、坚固性和程序的
自动移植性方面都有许多缺点。
从 ODBC C API 到 Java API 的字面翻译是不可取的。例如,Java 没有指针,而 ODBC 却对指针用得很广泛(包括很容易
出错的指针 "void *")。您可以将 JDBC 想象成被转换为面向对象接口的 ODBC,而面向对象的接口对 Java 程序员来说较
易于接收。
ODBC 很难学。它把简单和高级功能混在一起,而且即使对于简单的查询,其选项也极为复杂。相反,JDBC 尽量保证简单功
能的简便性,而同时在必要时允许使用高级功能。
启用“纯 Java ”机制需要象 JDBC 这样的 Java API。如果使用 ODBC,就必须手动地将 ODBC 驱动程序管理器和驱动程序
安装在每台客户机上。如果完全用 Java 编写 JDBC 驱动程序则 JDBC 代码在所有 Java 平台上(从网络计算机到大型机)
都可以自动安装、移植并保证安全性。
总之,JDBC API 对于基本的 SQL 抽象和概念是一种自然的 Java 接口。它建立在 ODBC 上而不是从零开始。因此,熟悉
ODBC 的程序员将发现 JDBC 很容易使用。JDBC 保留了 ODBC 的基本设计特征;事实上,两种接口都基于 X/Open SQL CLI
(调用级接口)。它们之间最大的区别在于:JDBC 以 Java 风格与优点为基础并进行优化,因此更加易于使用。

最近,Microsoft 又引进了 ODBC 之外的新 API: RDO、 ADO 和 OLE DB。这些设计在许多方面与 JDBC 是相同的,即它们
都是面向对象的数据库接口且基于可在 ODBC 上实现的类。但在这些接口中,我们未看见有特别的功能使我们要转而选择它
们来替代 ODBC,尤其是在 ODBC 驱动程序已建立起较为完善的市场的情况下。它们最多也就是在 ODBC 上加了一种装饰而
已。这并不是说 JDBC 不需要从其最初的版本再发展了;然而,我们觉得大部份的新功能应归入诸如前一节中所述的对象/
关系映射和嵌入式 SQL 这样的高级 API。


1.1.4 两层模型和三层模型
JDBC API 既支持数据库访问的两层模型,同时也支持三层模型。

在两层模型中,Java applet 或应用程序将直接与数据库进行对话。这将需要一个 JDBC 驱动程序来与所访问的特定数据库
管理系统进行通讯。用户的 SQL 语句被送往数据库中,而其结果将被送回给用户。数据库可以位于另一台计算机上,用户
通过网络连接到上面。这就叫做客户机/服务器配置,其中用户的计算机为客户机,提供数据库的计算机为服务器。网络可
以是 Intranet(它可将公司职员连接起来),也可以是 Internet。

在三层模型中,命令先是被发送到服务的“中间层”,然后由它将 SQL 语句发送给数据库。数据库对 SQL 语句进行处理并
将结果送回到中间层,中间层再将结果送回给用户。MIS 主管们都发现三层模型很吸引人,因为可用中间层来控制对公司数
据的访问和可作的的更新的种类。中间层的另一个好处是,用户可以利用易于使用的高级 API,而中间层将把它转换为相应
的低级调用。最后,许多情况下三层结构可提供一些性能上的好处。


到目前为止,中间层通常都用 C 或 C++ 这类语言来编写,这些语言执行速度较快。然而,随着最优化编译器(它把 Java
字节代码转换为高效的特定于机器的代码)的引入,用 Java 来实现中间层将变得越来越实际。这将是一个很大的进步,它
使人们可以充分利用 Java 的诸多优点(如坚固、多线程和安全等特征)。JDBC 对于从 Java 的中间层来访问数据库非常
重要。


1.1.5 SQL 的一致性
结构化查询语言 (SQL) 是访问关系数据库的标准语言。困难之处在于:虽然大多数的 DBMS (数据库管理系统)对其基本
功能都使用了标准形式的 SQL,但它们却不符合最近为更高级的功能定义的标准 SQL 语法或语义。例如,并非所有的数据
库都支持储存程序或外部连接,那些支持这一功能的数据库又相互不一致。人们希望 SQL 中真正标准的那部份能够进行扩
展以包括越来越多的功能。但同时 JDBC API 又必须支持现有的 SQL。

JDBC API 解决这个问题的一种方法是允许将任何查询字符串一直传到所涉及的 DBMS 驱动程序上。这意味着应用程序可以
使用任意多的 SQL 功能,但它必须冒这样的风险:有可能在某些 DBMS 上出错。事实上,应用程序查询甚至不一定要是
SQL,或者说它可以是个为特定的 DBMS 设计的 SQL 的专用派生物(例如,文档或图象查询)。

JDBC 处理 SQL 一致性问题的第二种方法是提供 ODBC 风格的转义子句。这将在 4.1.5 节“语句对象中的 SQL 转义语法”
中讨论。

转义语法为几个常见的 SQL 分歧提供了一种标准的 JDBC 语法。例如,对日期文字和已储存过程的调用都有转义语法。

对于复杂的应用程序,JDBC 用第三种方法来处理 SQL 的一致性问题。它利用 DatabaseMetaData 接口来提供关于 DBMS 的
描述性信息,从而使应用程序能适应每个 DBMS 的要求和功能。

由于 JDBC API 将用作开发高级数据库访问工具和 API 的基础 API,因此它还必须注意其所有上层建筑的一致性。“符合
JDBC 标准TM" 代表用户可依赖的 JDBC 功能的标准级别。要使用这一说明,驱动程序至少必须支持 ANSI SQL-2 Entry
Level(ANSI SQL-2 代表美国国家标准局 1992 年所采用的标准。Entry Level 代表 SQL 功能的特定清单)。驱动程序开
发人员可用 JDBC API 所带的测试工具包来确定他们的驱动程序是否符合这些标准。

“符合 JDBC 标准TM” 表示提供者的 JDBC 实现已经通过了 JavaSoft 提供的一致性测试。这些一致性测试将检查 JDBC
API 中定义的所有类和方法是否都存在,并尽可能地检查程序是否具有 SQL Entry Level 功能。当然,这些测试并不完全,
而且 JavaSoft 目前也无意对各提供者的实现进行标级。但这种一致性定义的确可对 JDBC 实现提供一定的可信度。随着越
来越多的数据库提供者、连接提供者、Internet 提供者和应用程序编程员对 JDBC API 的接受,JDBC 也正迅速成为 Java
数据库访问的标准。


1.2 JDBC 产品
在编写本文时,有几个基于 JDBC 的产品已开发完毕或正在开发中。当然,本节中的信息将很快成为过时信息。因此,有
关最新的信息,请查阅 JDBC 的网站,可通过从以下 URL 开始浏览找到:

http://java.sun.com/products/jdbc


1.2.1 JavaSoft 框架
JavaSoft 提供三种 JDBC 产品组件,它们是 Java 开发工具包 (JDK) 的组成部份:


JDBC 驱动程序管理器,


JDBC 驱动程序测试工具包,和


JDBC-ODBC 桥。


JDBC 驱动程序管理器是 JDBC 体系结构的支柱。它实际上很小,也很简单;其主要作用是把 Java 应用程序连接到正确的
JDBC 驱动程序上,然后即退出。

JDBC 驱动程序测试工具包为使 JDBC 驱动程序运行您的程序提供一定的可信度。只有通过 JDBC 驱动程序测试包的驱动程
序才被认为是符合 JDBC 标准TM 的。

JDBC-ODBC 桥使 ODBC 驱动程序可被用作 JDBC 驱动程序。它的实现为 JDBC 的快速发展提供了一条途径,其长远目标提
供一种访问某些不常见的 DBMS(如果对这些不常见的 DBMS 未实现 JDBC) 的方法。

1.2.2 JDBC 驱动程序的类型
我们目前所知晓的 JDBC 驱动程序可分为以下四个种类:


JDBC-ODBC 桥加 ODBC 驱动程序:JavaSoft 桥产品利用 ODBC 驱动程序提供 JDBC 访问。注意,必须将 ODBC 二进制代码
(许多情况下还包括数据库客户机代码)加载到使用该驱动程序的每个客户机上。因此,这种类型的驱动程序最适合于企业
网(这种网络上客户机的安装不是主要问题),或者是用 Java 编写的三层结构的应用程序服务器代码。
本地 API - 部份用 Java 来编写的驱动程序: 这种类型的驱动程序把客户机 API 上的 JDBC 调用转换为 Oracle、
Sybase、Informix、DB2 或其它 DBMS 的调用。注意,象桥驱动程序一样,这种类型的驱动程序要求将某些二进制代码加载
到每台客户机上。
JDBC 网络纯 Java 驱动程序:这种驱动程序将 JDBC 转换为与 DBMS 无关的网络协议,之后这种协议又被某个服务器转换
为一种 DBMS 协议。这种网络服务器中间件能够将它的纯 Java 客户机连接到多种不同的数据库上。所用的具体协议取决于
提供者。通常,这是最为灵活的 JDBC 驱动程序。有可能所有这种解决方案的提供者都提供适合于 Intranet 用的产品。为
了使这些产品也支持 Internet 访问,它们必须处理 Web 所提出的安全性、通过防火墙的访问等方面的额外要求。几家提
供者正将 JDBC 驱动程序加到他们现有的数据库中间件产品中。
本地协议纯 Java 驱动程序:这种类型的驱动程序将 JDBC 调用直接转换为 DBMS 所使用的网络协议。这将允许从客户机机
器上直接调用 DBMS 服务器,是 Intranet 访问的一个很实用的解决方法。由于许多这样的协议都是专用的,因此数据库提
供者自己将是主要来源,有几家提供者已在着手做这件事了。
最后,我们预计第 3、4 类驱动程序将成为从 JDBC 访问数据库的首选方法。第 1、2 类驱动程序在直接的纯 Java 驱动程
序还没有上市前将会作为过渡方案来使用。对第 1、2 类驱动程序可能会有一些变种(下表中未列出),这些变种要求有连
接器,但通常这些是更加不可取的解决方案。第 3、4 类驱动程序提供了 Java 的所有优点,包括自动安装(例如,通过使
用 JDBC 驱动程序的 applet applet来下载该驱动程序)。

下表显示了这 4 种类型的驱动程序及其属性:


驱动程序种类 纯 JAVA? 网络协议
1 - JDBC-OCBC 桥 非 直接
2 - 基于本地 API 的 非 直接
3 - JDBC 网络的 是 要求连接器
4 - 基于本地协议的 是 直接

1.2.3 JDBC 驱动程序的获取
在编写本文时,已有几十个属于种类的驱动程序,即可与 Javasoft 桥联合使用的 1: ODBC 驱动程序的驱动程序。有大约
十多个属于种类 2 的驱动程序是以 DBMS 的本地 API 为基础编写的。只有几个属于种类 3 的驱动程序。目前至少有 2 个
属于种类 4 的驱动程序,但到 1997 年底,我们预计主要的 DBMS 都会有种类 4 的驱动程序。

要获取关于驱动程序的最新信息,请查阅 JDBC 的网站,其网址为: http:// java.sun.com/products/jdbc。提供第 3 种
驱动程序的首批提供者是 SCO、Open Horizon、Visigenic 和 WebLogic。JavaSoft 和数据库连接的领先提供者 Intersolv
合作研制了 JDBC-ODBC 桥和 JDBC 驱动程序测试工具包。


1.2.4 其它产品
各种 JDBC 应用程序的开发工具正在开发中。请注意查阅 JavaSoft 网页以得到更新信息。


Valid XHTML 1.0 Strict and CSS. Powered by pLog
Design by Blog.lvwo.com