Ebean findPagedList() throw Exception的分析处理过程
一、定义User
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "t_user") public class User { @Id private String id; @Column private String username; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
二、执行测试
抛出Exception:Caused by: java.lang.IllegalStateException: Bean class User is not enhanced?@Test public void testFindPageList() { Query<User> find = Ebean.find(User.class); find.setMaxRows(1); find.setFirstRow(1); System.out.println(find.findPagedList().getList()); }
三、分析过程
step1
进入BeanDescriptorManager.java:1442查看错误处,看到抛出Exception这里有个hasEntityBeanInterface的校验:
进入到hasEntityBeanInterface方法,源码如下:
由此可知,Entity Bean必须实现一个EntityBean的接口(在网上看到的都没有实现这个接口和集成Model的,不知道是版本问题还是用的方式不对)private boolean hasEntityBeanInterface(Class<?> beanClass) { Class<?>[] interfaces = beanClass.getInterfaces(); Class[] var3 = interfaces; int var4 = interfaces.length; for(int var5 = 0; var5 < var4; ++var5) { Class<?> anInterface = var3[var5]; if (anInterface.equals(EntityBean.class)) { return true; } } return false; }
step2
User实现EntityBean接口:
执行测试,抛出Exception:import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import java.beans.PropertyChangeListener; @Entity @Table(name = "t_user") public class User implements EntityBean { @Id private String id; @Column private String username; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String[] _ebean_getPropertyNames() { return new String[0]; } public String _ebean_getPropertyName(int i) { return null; } public String _ebean_getMarker() { return null; } public Object _ebean_newInstance() { return null; } public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) { } public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) { } public void _ebean_setEmbeddedLoaded() { } public boolean _ebean_isEmbeddedNewOrDirty() { return false; } public EntityBeanIntercept _ebean_getIntercept() { return null; } public EntityBeanIntercept _ebean_intercept() { return null; } public void _ebean_setField(int i, Object o) { } public void _ebean_setFieldIntercept(int i, Object o) { } public Object _ebean_getField(int i) { return null; } public Object _ebean_getFieldIntercept(int i) { return null; } }
进入BeanPropertiesReader.java:43查看错误处,看到getProperties源码如下:
这里获取一个 public static的 _ebean_props字段,因为这个字段存储的是当前实体类( User)对应数据库的字段,所以用 field.get((Object)null)来限制该字段必须是 public static字段。private String[] getProperties(Class<?> clazz) { try { Field field = clazz.getField("_ebean_props"); return (String[])((String[])field.get((Object)null)); } catch (Exception var3) { throw new IllegalStateException("Error getting _ebean_props field on type " + clazz, var3); } }
step3
增加_ebean_props字段后,再次执行测试,抛出Exception:
进入到BeanDescriptor.java:523查看错误处,看到如下代码(这里的_ebean_getIntercept()就是User实现的EntityBean的_ebean_getIntercept()):
EntityBeanIntercept ebi = this.prototypeEntityBean._ebean_getIntercept(); this.idPropertyIndex = this.idProperty == null ? -1 : ebi.findProperty(this.idProperty.getName());
step4
重写 User中的 _ebean_getIntercept():执行测试,发现结果字段值均为null:public EntityBeanIntercept _ebean_getIntercept() { return new EntityBeanIntercept(this); }
step5Debugger跟踪源码运行,发现设置属性值的方法在EnhanceBeanPropertyAccess.Setter.set(...)方法中会调用实体类的_ebean_setField(...)方法对属性进行赋值操作(即赋值操作由EntityBean._ebean_setField(...)方法完成)。
重写User的_ebean_setField方法:
执行测试,运行结果如下:public void _ebean_setField(int i, Object o) { try { Field field = this.getClass().getDeclaredField(get_ebean_props()[i]); field.setAccessible(true); field.set(this, o); } catch (Exception e) { e.printStackTrace(); } }
四、整理后的代码
定义AbstractEntityBean,实现EntityBean接口:
import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import java.beans.PropertyChangeListener; import java.lang.reflect.Field; public abstract class AbstractEntityBean implements EntityBean { // 因为每个实体类都需要一个静态的_ebean_props属性,所以这里定义一个方法获取这个静态属性方便下面的代码调用 public abstract String[] get_ebean_props(); public String[] _ebean_getPropertyNames() { return get_ebean_props(); } public String _ebean_getPropertyName(int i) { return null; } public String _ebean_getMarker() { return null; } public abstract Object _ebean_newInstance(); public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) { } public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) { } public void _ebean_setEmbeddedLoaded() { } public boolean _ebean_isEmbeddedNewOrDirty() { return false; } public EntityBeanIntercept _ebean_getIntercept() { return new EntityBeanIntercept(this); } public EntityBeanIntercept _ebean_intercept() { return null; } public void _ebean_setField(int i, Object o) { try { Field field = this.getClass().getDeclaredField(get_ebean_props()[i]); field.setAccessible(true); field.set(this, o); } catch (Exception e) { e.printStackTrace(); } } public void _ebean_setFieldIntercept(int i, Object o) { } public Object _ebean_getField(int i) { return null; } public Object _ebean_getFieldIntercept(int i) { return null; } }
修改User,继承AbstractEntityBean,实现EntityBean接口(因为Class.getInterfaces()只能获取到当前类实现的接口,所以这里再实现一次):
import io.ebean.bean.EntityBean; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "t_user") public class User extends AbstractEntityBean implements EntityBean { public static String[] _ebean_props = new String[] {"id", "username"}; @Id @Column(name = "id") private String id; @Column private String username; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String[] get_ebean_props() { return _ebean_props; } @Override public Object _ebean_newInstance() { return new User(); } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", username='" + username + '\'' + '}'; } }
测试代码TestDBConnection
import io.ebean.Ebean; import io.ebean.Query; import org.junit.Test; public class TestDBConnection { @Test public void test() { System.out.println("cnt : " + Ebean .createSqlQuery("select count(0) as cnt from t_user").findOne() .getInteger("cnt")); } @Test public void testFindPageList() { Query<User> find = Ebean.find(User.class); find.setMaxRows(1); find.setFirstRow(1); System.out.println(find.findPagedList().getList()); } }
五、总结
这种不与其他框架集成的方式:
- 实体类需要实现EntityBean接口
- 实体类需要有一个public static的_ebean_props字段存储查询的字段名
- 实体类中的_ebean_getIntercept()返回值不能为null(即不能是空实现)
- 查询数据后Ebean会调用_ebean_setField方法为属性赋值,实体类中的_ebean_setField方法不能是空实现