Ignore those warnings. Tomcat is doing its job right. The actual bug is in someone else's code (the JDBC driver in question), not in yours. Be happy that Tomcat did its job properly and wait until the JDBC driver vendor get it fixed so that you can upgrade the driver. On the other hand, you aren't supposed to drop a JDBC driver in webapp's /WEB-INF/lib
, but only in server's /lib
. If you still keep it in webapp's /WEB-INF/lib
, then you should manually register and deregister it using a ServletContextListener
.
忽略警告。把WEB-INF/lib下的mysql驅動文件拷貝到Tomcat/lib下。如果仍然要放在WEB-INF/lib下,需要使用監聽器手動的注冊和注銷。
下面的文章介紹如何寫監聽器,http://javabeat.net/servletcontextlistener-example/, 當然如果是Servlet3.0, 使用注解方式設置監聽也是可以的。
下面的代碼是如何注銷。
Downgrade to Tomcat 6.0.23 or older so that you will not be bothered with those warnings. But it will silently keep leaking memory. Not sure if that's good to know after all. Those kind of memory leaks are one of the major causes behind OutOfMemoryError
issues during Tomcat hotdeployments.
把Tomcat降級到低版本(6.0.23以下),雖然不會報錯,但是還是存在內存益出的問題,這并不是一個好的解決方案。
Move the JDBC driver to Tomcat's /lib
folder and have a connection pooled datasource to manage the driver. Note that Tomcat's builtin DBCP does not deregister drivers properly on close. See also bug DBCP-322 which is closed as WONTFIX. You would rather like to replace DBCP by another connection pool which is doing its job better then DBCP. For exampleHikariCP, BoneCP, or perhaps Tomcat JDBC Pool.
把驅動文件移到Tomcat/lib文件夾下,不用使用DBCP,使用以下的連接池庫,HikariCP, BoneCP,或者Tomcat JDBC Pool.
If you are getting this message from a Maven built war change the scope of the JDBC driver to provided, and put a copy of it in the lib directory. Like this:<dependency>,
對于MAVEN項目,由于Tomcat中存在mysql驅動文件(1中介紹),這樣在部署時就不會把mysql帶到打包文件里,注意是<scope>provided</scope>。
示例:this.getHibernateTemplate().find("from bean.User");
返回所有User對象
二、find(String queryString , Object value);
示例:this.getHibernateTemplate().find("from bean.User u where u.name=?", "test");
或模糊查詢:this.getHibernateTemplate().find("from bean.User u where u.name like ?", "%test%");
返回name屬性值為test的對象(模糊查詢,返回name屬性值包含test的對象)
三、find(String queryString, Object[] values);
示例:String hql= "from bean.User u where u.name=? and u.password=?"
this.getHibernateTemplate().find(hql, new String[]{"test", "123"});
返回用戶名為test并且密碼為123的所有User對象
---------------------------------
四、findByExample(Object exampleEntity)
示例:
User u=new User();
u.setPassword("123");//必須 符合的條件但是這兩個條件時并列的(象當于sql中的and)
u.setName("bb");
list=this.getHibernateTemplate().findByExample(u,start,max);
返回:用戶名為bb密碼為123的對象
五、findByExample(Object exampleEntity, int firstResult, int maxResults)
示例:
User u=new User();
u.setPassword("123");//必須 符合的條件但是這兩個條件時并列的(象當于sql中的and)
u.setName("bb");
list=this.getHibernateTemplate().findByExample(u,start,max);
返回:滿足用戶名為bb密碼為123,自start起共max個User對象。(對象從0開始計數)
---------------------------------------------------
六、findByNamedParam(String queryString , String paramName , Object value)
使用以下語句查詢:
String queryString = "select count(*) from bean.User u where u.name=:myName";
String paramName= "myName";
String value= "xiyue";
this.getHibernateTemplate().findByNamedParam(queryString, paramName, value);
System.out.println(list.get(0));
返回name為xiyue的User對象的條數
七、findByNamedParam(String queryString , String[] paramName , Object[] value)
示例:
String queryString = "select count(*) from bean.User u where u.name=:myName and u.password=:myPassword";
String[] paramName= new String[]{"myName", "myPassword"};
String[] value= new String[]{"xiyue", "123"};
this.getHibernateTemplate().findByNamedParam(queryString, paramName, value);
返回用戶名為xiyue密碼為123的User對象
八、findByNamedQuery(String queryName)
示例:
1、首先需要在User.hbm.xml中定義命名查詢
<hibernate-mapping>
<class>......</class>
<query name="queryAllUser"><!--此查詢被調用的名字-->
<![CDATA[
from bean.User
]]>
</query>
</hibernate-mapping>
2、如下使用查詢:
this.getHibernateTemplate().findByNamedQuery("queryAllUser");
九、findByNamedQuery(String queryName, Object value)
示例:
1、首先需要在User.hbm.xml中定義命名查詢
<hibernate-mapping>
<class>......</class>
<query name="queryByName"><!--此查詢被調用的名字-->
<![CDATA[
from bean.User u where u.name = ?
]]>
</query>
</hibernate-mapping>
2、如下使用查詢:
this.getHibernateTemplate().findByNamedQuery("queryByName", "test");
十、findByNamedQuery(String queryName, Object[] value)
示例:
1、首先需要在User.hbm.xml中定義命名查詢
<hibernate-mapping>
<class>......</class>
<query name="queryByNameAndPassword"><!--此查詢被調用的名字-->
<![CDATA[
from bean.User u where u.name =? and u.password =?
]]>
</query>
</hibernate-mapping>
2、如下使用查詢:
String[] values= new String[]{"test", "123"};
this.getHibernateTemplate().findByNamedQuery("queryByNameAndPassword" , values);
十一、findByNamedQueryAndNamedParam(String queryName, String paramName, Object value)
示例:
1、首先需要在User.hbm.xml中定義命名查詢
<hibernate-mapping>
<class>......</class>
<query name="queryByName"><!--此查詢被調用的名字-->
<![CDATA[
from bean.User u where u.name =:myName
]]>
</query>
</hibernate-mapping>
2、如下使用查詢:
this.getHibernateTemplate().findByNamedQuery("queryByName" , "myName", "test");
十二、findByNamedQueryAndNamedParam(String queryName, String[] paramName, Object[] value)
this.getHibernateTemplate().findByNamedQuery("queryByNameAndPassword" , names, values);
十三、findByValueBean(String queryString , Object value);
示例:
1、定義一個ValueBean,屬性名必須和HSQL語句中的:后面的變量名同名,此處必須至少有兩個屬性,分別為myName和 myPassword,使用setter方法設置屬性值后
ValueBean valueBean= new ValueBean();
valueBean.setMyName("test");
valueBean.setMyPasswrod("123");
2、
String queryString= "from bean.User u where u.name=:myName and u.password=:myPassword";
this.getHibernateTemplate().findByValueBean(queryString , valueBean);
十四、findByNamedQueryAndValueBean(String queryName , Object value);
示例:
1、首先需要在User.hbm.xml中定義命名查詢
<hibernate-mapping>
<class>......</class>
<query name="queryByNameAndPassword"><!--此查詢被調用的名字-->
<![CDATA[
from bean.User u where u.name =:myName and u.password=:myPassword
]]>
</query>
</hibernate-mapping>
2、定義一個ValueBean,屬性名必須和User.hbm.xml命名查詢語句中的:后面的變量名同名,此處必須至少有兩個屬性,分別為 myName和myPassword,使用setter方法設置屬性值后
ValueBean valueBean= new ValueBean();
valueBean.setMyName("test");
valueBean.setMyPasswrod("123");
3、
String queryString= "from bean.User u where u.name=:myName and u.password=:myPassword";
this.getHibernateTemplate().findByNamedQueryAndValueBean("queryByNameAndPassword", valueBean);
The session attributes are available on the ActionContext instance, which is made available via ThreadLocal. _Preferred_
Preferred
Preferred
在windows操作系統下(當然linux也可以,linux是在終端下)cd到項目的目錄,mvn jetty:run, 即可看到系統。
源碼付在下面,請大家參考。
用eclipse導入后,修改hibernate.cfg.xml里面的mysql的配置,首先要先建立相應的數據庫,然后運行cn.edu.nku.common.ExportDB.java生成數據庫里的表,。
有一點還希望大家能給出指點,用maven+jetty部署項目, 每次都要關閉jetty,然后再運行mvn jetty:run,才能重新部署,不知道有沒有好的辦法,進行熱部署。希望高人能指點迷津。
源碼下載
First check out the previous tutorial on creating a Blog application to get yourself going. In this article, I'll show you how to add forms and a bit of interactivity. You will learn the following Warp concepts:
Continuing from the previous tutorial, let's now make a "compose new entry" page. This will be very similar to the other pages with some different components:
<html>
<head w:component="meta">
<title>Warp :: Compose New Blog Entry</title>
</head><body w:component="frame">
<input w:component="textfield" w:bind="newBlog.subject" />
<input w:component="textarea" w:bind="newBlog.text" /><input w:component="button" w:label="post blog" />
</body></html>
The first important part to notice is that the <head> tag is decorated with a Meta component. This is very important indeed--along with the Frame component on <body>, Meta forms the foundation for Warp page behavior.
The other things to notice are the input components. TextField is a simple text box, we use the attribute w:bind to tell Warp where to bind the user input to. In this case I am giving it a path to a property of newBlog, which is a variable in my page object. OK, let's create ourselves this property:
@URIMapping("/blogs/compose")
public class ComposeBlog {
private Blog newBlog = new Blog("", ""); //an empty blog
@OnEvent
public void save() {
System.out.println(newBlog.getSubject() + " - "
+ newBlog.getText());
}
public Blog getNewBlog() {
return newBlog;
}
}
Here I have provided an event handler method named save(). By tagging it with the @OnEvent annotation,
I have told Warp to invoke this method whenever the page triggers an
event (in our case, the clicking of the button). First, the data is
synchronized between the input components (TextField and TextArea) and the bound properties, then the event handler is fired which prints out their content.
Let's add some more functionality, where our list of blogs (from the previous tutorial) actually gets updated. For this we first need to add a method in ListBlogs that stores a new blog entry into the map. That part is easy enough:
@URIMapping("/home")
public class ListBlogs {
private Map<String, Blog> blogs = new HashMap<String, Blog>();
public ListBlogs() { .. }
public void addNewBlog(Blog blog) {
blogs.put(blog.getSubject(), blog);
}//...
}
Ok
now let us invoke the store method from our compose page's event
handler (remember Page-injection from the previous tutorial):
@URIMapping("/blogs/compose")
public class ComposeBlog {
private Blog newBlog = new Blog("", ""); //an empty blog
@Inject @Page ListBlogs listBlogs;
@OnEvent
public void save() {
listBlogs.addNewBlog(newBlog);
}
public Blog getNewBlog() {
return newBlog;
}
}
We're almost there, now when I save the entry, I want it to come back to the blog list. This follows the post-and-redirect design
pattern common in web development. Warp supports this in an intuitive,
type-safe manner. After saving the blog in my event handler, I simply
return the page object that I want shown:
@OnEvent
public ListBlogs save() {
listBlogs.addNewBlog(newBlog);
return listBlogs;
}
Neat! Now when you click the "post blog" button, it runs the save()
method and redirects you to back to the blog list. You can return any
type of object from an event handler so long as it is a page object (or
a subclass of one). You can also redirect to an arbitrary URL or use
JSP-style forwarding instead of post-and-redirect. Check out the Event Handling guide on the wiki for details.
One last step concerning Page scoping.
Typically, Warp obtains an instance of a page object from the Guice
injector on every request. This effectively means that any page object
that is not bound (see Guice user guide for information on scopes) with
a given scope is instantiated once per request. Since our HashMap is a
property of the ListBlogs page, this means that when we redirect (in a
new request), the Map gets blown away and we lose our new entry.
To
fix this, we can scope the ListBlogs page object as a singleton. This
means that ListBlogs is only created once for the lifetime of the
webapp, and the Map of entries is retained.
@URIMapping("/home")
@Singleton
public class ListBlogs { .. }
You should be careful about relying on page objects to maintain state and do a lot of thinking before declaring a scope on a page.
The
singleton scope is an easy fix for our example but in the real world
you will want to store the blogs in a more permanent storage medium
(such as a database or persistent store). In my next tutorial, we'll
see how to do just that using JPA and Hibernate.
First check out the Hello World example to get yourself going. In this article, I'll show you how to knock together a simple Blog application where you can browse and read blog entries. You will learn the following Warp concepts:
First let's create ourselves a data model object representing a blog. This will be a simple POJO with 2 fields: subject and text.
public class Blog {
private String subject;
private String text;
//don't forget your getters/setters here...
}
OK, now we make a list page where you can see a list of all
the blogs currently in the system. For simplicity, we will store blogs
in a HashMap. The Page object class for this list should look something
like this:
@URIMapping("/blogs")
public class ListBlogs {
private final Map<String, Blog> blogs = new HashMap<String, Blog>();
public ListBlogs() {
//setup a blog as dummy data
blog = new Blog("MyBlog", "Warp is so great...");
blogs.put(blog.getSubject(), blog);
}
public Collection<Blog> getBlogList() {
return blogs.values();
}
}
Note the @URIMapping annotation which tells warp to map this page object to the URI "/blogs". The getter for property blogList, simply returns a list of all the values contained in our HashMap of blogs. For the interesting part, let's make a template to layout our blogs:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>Warp :: Blogs</title>
</head>
<body w:component="frame">
<h1>A list of blog entries</h1>
<table w:component="table" w:items="${blogList}"/>
</body>
</html>
This is a very simple template. Inside our body we have only one real component (Table) and we pass it our list of blogs in the w:items property that we declared above. Go ahead and run the app now and point your browser to: http://localhost:8080/blogs, you should see a table with 2 columns (subject and text) corresponding to the Blog data model object we declared above. Table is smart enough to inspect the items given it and construct an appropriate set of display columns. The rows of the table reflect the data in each instance of the Blog object in our HashMap. Nice!
OK, having the
property names as the title of each column is not ideal--let's
customize this. Customizing is as simple as adding a properties file in
the same package as our Blog class, with the same name:
Blog.properties:
subject=Subject of Blog
text=Content
Now, run the app again and you should see the column names changed. In this way, you can also achieve internationalization (i18n). If you want to hide a property (i.e. not render a column for it), specify an empty key (so to hide subject, you would have just "subject=").
OK, let's create a view blog page now. This page will display one blog entry. Let's start with the view portion (called ViewBlog.html):
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head w:component="meta">
<title>Reading :: ${blog.subject}</title>
</head>
<body w:component="frame">
<h1>Read...</h1>
<h3>${blog.subject}</h3>
${blog.text}
<a href="../home">back to list</a>
</body>
And its Page object:
@URIMapping("/blog")
public class ViewBlog {
private Blog blog;
//getters/setters...
}
Ok, this is fairly simple, but how does it know what blog to display? Let us use a RESTful idiom to achieve this. First, change your ViewBlog page object so that it takes a parameter indicating what blog to show:
@URIMapping("/blog/{subject}")
public class ViewBlog {
private Blog blog;
@OnEvent @PreRender
public void init(String subject) { .. }
}
Ok, this tells warp that when the URL http://localhost:8080/blog/myblog is requested, to inject anything matching the {subject} portion of the @URIMapping (in this case "myblog") to the @PreRender event handler method. Hold on, we're not done yet--we still need to obtain the appropriate blog from our HashMap which is stored in the ListBlogs page. This is done via page-injection:
@URIMapping("/blog/{subject}")
public class ViewBlog {
private Blog blog;
@Inject @Page private ListBlogs listBlogs;
@OnEvent @PreRender
public void init(String subject) {
this.blog = listBlogs.getBlog(subject);
}
}
Finally, we need to modify ListBlogs and give it a getBlog() method to fetch a Blog by subject:
@URIMapping("/blogs")
public class ListBlogs {
private Map<String, Blog> blogs = new HashMap<String, Blog>();
public ListBlogs() { .. }
public Collection<Blog> getBlogList() {
return blogs.values();
}
public Blog getBlog(String subject) {
return blogs.get(subject);
}
}
Ok,
now let's wire the pages together so clicking on a blog in the list
will take us to the view page for that blog. I want to make the subject
of the blog clickable, so let's use the Column component to override
the default behavior of table (which just prints out the subject as
text):
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head w:component="meta">
<title>Warp :: Blogs</title>
</head>
<body w:component="frame">
<h1>A list of blog entries</h1>
<table w:component="table" w:items="${blogList}" class="mytableCss">
<td w:component="column" w:property="subject">
<a w:component="hyperlink" w:target="/blog"
w:topic="${subject}">${subject}</a>
</td>
</table>
</body>
</html>
Notice that we nest the hyperlink component inside the column override. This tells the Table component not to draw the column, instead to use our overridden layout instead. The attribute w:target simply tells the hyperlink the URI that we're linking (in this case /blog, which is the ViewBlog's mapping) and w:topic tells hyperlink to append the subject of the blog to the URI. So for a blog entitled "MyBlog," Warp will generate a URI as follows: /blog/MyBlog. And "MyBlog" gets stripped out and injected into the ViewBlog page's @PreRender handler so it can set it up.
Also notice the addition of a non-Warp attribute class to the <table> tag, which refers to the CSS class I want to style my table with. This is a useful tool for both for previewability as well as the end result HTML--generally whatever HTML attributes you write on a component will be passed through the final rendered page.
Done!First download warp and its dependencies from the link provided above. For convenience, dependencies are packaged along with the warp core library in a zip file named warp-with-deps.zip. Unzip and place the provided jars into your application's WEB-INF/lib folder. Create a web.xml file in WEB-INF, and add the following filter mapping to it:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
<context-param>
<param-name>warp.module</param-name>
<param-value>my.example.MyModule</param-value>
</context-param>
<filter>
<filter-name>warp</filter-name>
<filter-class>com.wideplay.warp.WarpFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>warp</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Note that the parameter warp.module must specify the class name of your Warp Module class. This is typically a class that looks like so:
package my.example;
public class MyModule implements WarpModule {
public void configure(Warp warp) {
//nothing required yet
}
}
Now, this tells Warp to look in package my.example for page classes (and resources) to register to the runtime. Every class in the WarpModule's package and its sub-packages are considered candidates for page object registration (but only those that have corresponding templates are typically registered). You can also perform a lot of setup work in the WarpModule as we will see later on.
In Warp, a page is represented by a page object, and any manipulation of that page or its state is driven from the page object. OK, let's create a simple page to say hello to the world:
package my.example;
public class Start {
private String message = "hello warp!";
//getter...
public String getMessage() { return message; }
}
In Warp, page objects are always be accompanied by a template (in this case a standard HTML template) to tell Warp how to decorate and layout the content in the page. As a convention, templates are named the same as page classes (in our case, Start.html in the / directory):
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns:w="http://www.wideplay.com/warp/schema/warp_core.xsd"
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en" lang="en">
<head>
<title>Warp :: Hello World</title>
</head>
<body w:component="frame">
<p>
${message}
</p>
</body>
</html>
Note that the only unusual part about this template is the attribute on body named w:component which tells Warp to decorate the <body> tag with a Frame component. The Frame component is used on nearly every page and is a kind of "wrapper" for the page (and should always be present on the <body> tag) which creates some necessary conditions for the rendering of the page by the Warp framework.
The other noteworthy part of the template is the ${message} expression, which is a property expression telling Warp to look into the corresponding page object for a property named message. If you look above, we've declared message as a String in our Start page class.
You should now have a web application with roughly the following structure:
/
- Start.html
- WEB-INF/
- web.xml
+ lib/
+ classes/
Running the web aplication and pointing your browser at http://localhost:8080/Start now will produce the following page output:
struts.configuration
The org.apache.struts2.config.Configuration implementation class
org.apache.struts2.config.Configuration接口名
struts.configuration.files
A list of configuration files automatically loaded by Struts
struts自動加載的一個配置文件列表
struts.configuration.xml.reload
Whether to reload the XML configuration or not
是否加載xml配置(true,false)
struts.continuations.package
The package containing actions that use Rife continuations
含有actions的完整連續的package名稱
struts.custom.i18n.resources
Location of additional localization properties files to load
加載附加的國際化屬性文件(不包含.properties后綴)
struts.custom.properties
Location of additional configuration properties files to load
加載附加的配置文件的位置
struts.devMode
Whether Struts is in development mode or not
是否為struts開發模式
struts.dispatcher.parametersWorkaround
Whether to use a Servlet request parameter workaround necessary for some versions of WebLogic
(某些版本的weblogic專用)是否使用一個servlet請求參數工作區(PARAMETERSWORKAROUND)
struts.enable.DynamicMethodInvocation
Allows one to disable dynamic method invocation from the URL
允許動態方法調用
struts.freemarker.manager.classname
The org.apache.struts2.views.freemarker.FreemarkerManager implementation class
org.apache.struts2.views.freemarker.FreemarkerManager接口名
struts.i18n.encoding
The encoding to use for localization messages
國際化信息內碼
struts.i18n.reload
Whether the localization messages should automatically be reloaded
是否國際化信息自動加載
struts.locale
The default locale for the Struts application
默認的國際化地區信息
struts.mapper.class
The org.apache.struts2.dispatcher.mapper.ActionMapper implementation class
org.apache.struts2.dispatcher.mapper.ActionMapper接口
struts.multipart.maxSize
The maximize size of a multipart request (file upload)
multipart請求信息的最大尺寸(文件上傳用)
struts.multipart.parser
The org.apache.struts2.dispatcher.multipart.
MultiPartRequest parser implementation for a multipart request (file upload)
專為multipart請求信息使用的org.apache.struts2.dispatcher.multipart.MultiPartRequest解析器接口(文件上傳用)
struts.multipart.saveDir
The directory to use for storing uploaded files
設置存儲上傳文件的目錄夾
struts.objectFactory
The com.opensymphony.xwork2.ObjectFactory implementation class
com.opensymphony.xwork2.ObjectFactory接口(spring)
struts.objectFactory.spring.autoWire
Whether Spring should autoWire or not
是否自動綁定Spring
struts.objectFactory.spring.useClassCache
Whether Spring should use its class cache or not
是否spring應該使用自身的cache
struts.objectTypeDeterminer
The com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation class
com.opensymphony.xwork2.util.ObjectTypeDeterminer接口
struts.serve.static.browserCache
If static content served by the Struts filter should set browser caching header properties or not
是否struts過濾器中提供的靜態內容應該被瀏覽器緩存在頭部屬性中
struts.serve.static
Whether the Struts filter should serve static content or not
是否struts過濾器應該提供靜態內容
struts.tag.altSyntax
Whether to use the alterative syntax for the tags or not
是否可以用替代的語法替代tags
struts.ui.templateDir
The directory containing UI templates
UI templates的目錄夾
struts.ui.theme
The default UI template theme
默認的UI template主題
struts.url.http.port
The HTTP port used by Struts URLs
設置http端口
struts.url.https.port
The HTTPS port used by Struts URLs
設置https端口
struts.url.includeParams
The default includeParams method to generate Struts URLs
在url中產生 默認的includeParams
struts.velocity.configfile
The Velocity configuration file path
velocity配置文件路徑
struts.velocity.contexts
List of Velocity context names
velocity的context列表
struts.velocity.manager.classname
org.apache.struts2.views.velocity.VelocityManager implementation class
org.apache.struts2.views.velocity.VelocityManager接口名
struts.velocity.toolboxlocation
The location of the Velocity toolbox
velocity工具盒的位置
struts.xslt.nocache
Whether or not XSLT templates should not be cached
是否XSLT模版應該被緩存