这次是一个简单的登录界面,前台由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); } }这样,即使结果集为空,运行时也不会抛出异常了。