这次是一个简单的登录界面,前台由jquery.form负责判断输入是否非空以及无刷新显示后台登录信息。就这么简单,但是中间还是碰到了一个问题,困扰了很久。
先来看看我最初的代码:
一、数据库查询
用的是guice-persist的DynamicFinder,所以只需要实现一个接口,而不需要具体实现。配置方法见本人前面的文章《sitebricks 学习笔记之guice-persist的配置和使用》。
UserFinder.java:
public interface UserFinder {
@Finder(query = "select u from User u where u.name=:name and u.password=:password")
User authenticated(@Named("name") String name, @Named("password") String password);
}
二、前台登录界面:
使用了sitebricks的decoration技术,详细方法见《sitebricks学习笔记之decoration》。
Login.java:
@Decorated
public class Login extends Decorator {
public Login() {
}
@Override
public String getPageTitle(){
return "Bricks - 用户登录";
}
}
Login.html:
<!DOCTYPE HTML>
<html>
<head>
<title>none</title>
@Require
<script type="text/javascript" charset="UTF-8" src="static/js/jquery-1.7.2.min.js"></script>
@Require
<script type="text/javascript" charset="UTF-8" src="static/js/jquery.form.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function()
{
$('#loginform').submit(function()//提交表单
{
var options = {
target:'#msg',
url:'/hello', //提交给哪个执行
type:'POST',
beforeSubmit: validate,
success: function(data){ $('#msg').html(data.info);} //显示操作提示
};
$('#loginform').ajaxSubmit(options);
return false; //为了不刷新页面,返回false,反正都已经在后台执行完了,没事!
});
}
);
function validate(formData, jqForm, options) {
for (var i=0; i < formData.length; i++) {
if (!formData[i].value) {
$('#msg').html('请输入用户名和密码!');
return false;
}
}
}
</script>
<div class="login_container" id="login">
<div id="msg"></div>
<div class="login_box">
<div class="logo_title">
<h1><img src="/static/images/login_title.gif"/>用户登录</h1>
</div>
<div class="login_operate">
<form id="loginform" method="post" name="loginform">
<div class="username">
<label class="txt_default" for="name">用户名</label>
<input class="txt alias" id="name" name="name" type="text" tabindex="1"/>
</div>
<div class="password">
<label class="txt_default" for="password">密 码</label>
<input class="txt password" id="password" name="password" type="password" tabindex="2"/>
</div>
<div class="about_password">
<input class="remerber_password" id="remerber_password" type="checkbox" disabled/>
<label for="remerber_password">记住登录状态</label>
<a class="forgetPassword" href="" target="_blank">忘记密码?</a>
</div>
<div class="login_submit">
<a class="login_btn_wrapper" href=""><input class="login_btn" type="submit" value="登录"/></a>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
这里注意两点:1、由于使用了decoration,所以引入的两个必需的js文件,必须使用@Require,不然的话在最终输出的页面中将不会出现这两个js文件。
2、ajax form的具体执行代码需要放在<body>内,原因同上。
三、后台处理
Hello.java:
public class Hello {
@Inject
private UserFinder finder;
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Post
public Reply<Map<String, String>> hello() {
User user = finder.authenticated(name, password);
Map<String, String> result = new HashMap<String, String>();
if (user!=null)
result.put("info", "用户" + name + "登录成功!");
else result.put("info", "用户名或密码错误!");
return Reply.with(result).as(Json.class);
}
}
这里注意不要忘了name和password的getter/setter。
四、运行时问题出现
当数据库中不存在所查询的对象时,将会抛出NoResultException。
五、解决办法:
方案一:最初的时候,我用的办法是自己写一个Dao,在获取结果时使用try-catch,当jpa返回NoResultException时,使user=null。
UserDao.java:
public class UserDao{
@Inject
private EntityManager em;
public User authencated(String name, String password){
Query q = em.createQuery("select u from User where u.name=:name and u.password=:password");
User user = null;
try{
user = (User) q.getSingleResult();
return user;
}catch(NoResultException e){
return null;
}
finally{return user;}
}
}
然后将Hello.java中的
改为
其他代码不变,运行正常。但是总觉得这样风格太不统一,就像是一个hack。于是google了一下,发现guice-persist的这个问题与warp-persist是一脉相承的,出现NoresultException其实不是@Finder的问题,而是jpa规范的要求。那么,就没有解决办法了吗?warp-persist&sitebricks的作者dhanji告诉了我们解决方案。
方案二:
很简单,只需要修改@Finder的返回类型为ArrayList<User>,然后在Hello.java修改一下判断语句即可。
UserFinder.java:
public interface UserFinder {
@Finder(namedQuery = "getUserByNamePwd", returnAs = ArrayList.class)
ArrayList<User> authenticated(@Named("name") String name, @Named("password") String password);
}
注意,@Finder里必须指明returnAs。
Hello.java:
public class Hello {
@Inject
private UserFinder finder;
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Post
public Reply<Map<String, String>> hello() {
ArrayList<User> users = finder.authenticated(name, password);
System.out.println("\n>>>[BRICKS] Hello world!");
Map<String, String> result = new HashMap<String, String>();
if (!users.isEmpty())
result.put("info", "用户" + name + "登录成功!");
else result.put("info", "用户名或密码错误!");
return Reply.with(result).as(Json.class);
}
}
这样,即使结果集为空,运行时也不会抛出异常了。