模型对象
leap-orm支持使用ActiveRecord模式。ActiveRecord是由Rails提出的一种ORM模式,在单表的数据操作中优势非常明显。 同时leap也支持使用传统的dao模式的数据库操作方式,弥补了ActiveRecord模式在多表查询中的劣势。
创建数据库表
数据源配置完成之后,我们现在要创建数据库表,作为示例,我们创建两个实体表和一个中间表就可以了,模型图如下:
┌────────────────────┐ ┌────────────────────┐
│ leap_user │ │ leap_post │
├────────────────────┤ ├────────────────────┤
│id:varchar(36) │ │id:varchar(36) │
│name:varchar(50) │1 1 │name:varchar(50) │
│age:int(3) ├─────┐ ┌─────┤descript:varchar(50)│
│login_id:varchar(50)│ │ │ │created_at:timestamp│
│password:varchar(50)│ │ │ └────────────────────┘
│created_at:timestamp│ │ │
└────────────────────┘ │ │
│ │
│ │
│ │
│n │n
┌────┴──────────┴────┐
│ leap_user_post │
├────────────────────┤
│user_id:varchar(36) │
│post_id:varchar(36) │
│created_at:timestamp│
└────────────────────┘
对应的数据库创建sql:
CREATE DATABASE `leap`;
USE `leap`;
DROP TABLE IF EXISTS `leap_post`;
CREATE TABLE `leap_post` (
`id` varchar(36) NOT NULL,
`name` varchar(50) NOT NULL,
`descript` varchar(50) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `leap_user`;
CREATE TABLE `leap_user` (
`id` varchar(36) NOT NULL,
`name` varchar(50) NOT NULL,
`age` int(3) NOT NULL,
`login_id` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `leap_user_post`;
CREATE TABLE `leap_user_post` (
`user_id` varchar(36) NOT NULL,
`post_id` varchar(36) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`,`post_id`),
KEY `post_id` (`post_id`),
CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `leap_user` (`id`),
CONSTRAINT `post_id` FOREIGN KEY (`post_id`) REFERENCES `leap_post` (`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据库表和对象模型的映射
在进行数据库的增删改查之前,我们需要先创建应用与数据库表映射的模型,假设我们有User
和Post
两个类。这两个类都必须继承leap.orm.model.Model
类,这个类提供了所有ActiveRecord模式的所有数据操作接口。
为了保证映射关系,User.java
和Post.java
需要相应的属性和数据库表字段对应,User.java
和Post.java
的源代码如下:
User.java:
package leap.project.model;
import java.sql.Timestamp;
import leap.orm.annotation.Column;
import leap.orm.annotation.Id;
import leap.orm.annotation.Table;
import leap.orm.model.Model;
@Table("leap_user")
public class User extends Model {
@Id
@Column(name="id")
private String id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
private String loginId;
@Column(name="password")
private String password;
private Timestamp createdAt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getLoginId() {
return loginId;
}
public void setLoginId(String loginId) {
this.loginId = loginId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
}
Post.java:
package leap.project.model;
import java.sql.Timestamp;
import leap.orm.annotation.Column;
import leap.orm.annotation.Id;
import leap.orm.annotation.Table;
import leap.orm.model.Model;
@Table("leap_post")
public class Post extends Model {
@Id
@Column(name="id")
private String id;
@Column(name="name")
private String name;
@Column(name="descript")
private String descript;
private Timestamp createdAt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescript() {
return descript;
}
public void setDescript(String descript) {
this.descript = descript;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
}
从源码中我们可以看到,在类名上有一个@Table
的注解,在各个属性上也有@Column
的注解,相信这里非常好理解各个注解的意思.
需要说明的是,实际上注解并非必须的,leap会扫描所有的Model类并自动和数据库映射,只是在映射的时候遵循ORM默认映射规则,这里因为我们的数据库表有前缀leap_
因此不能按照默认规则映射,所以需要添加注解.
根据数据库模型图,User和Post是多对多关系,我们还需要一个UserPost.java
的Model来映射中间表:
package leap.project.model;
import java.sql.Timestamp;
import leap.orm.annotation.Id;
import leap.orm.annotation.Table;
import leap.orm.model.Model;
@Table("leap_user_post")
public class UserPost extends Model {
@Id
private String userId;
@Id
private String postId;
private Timestamp createdAt;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPostId() {
return postId;
}
public void setPostId(String postId) {
this.postId = postId;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
}
- 默认情况下,所有的Model子类和属性都会被对应成数据库的表或者字段
- 如果某些类或者属性不希望被映射到数据库表或字段,则可以用
@NonEntity
和@NonColumn
注解来标示指定的字段或类不做数据库映射.
Model属性自动填充
在我们创建模型时,常用的有两个字段created_at
和updated_at
,分别表示创建时间和修改时间,对于这两个常见的字段,leap提供了便捷的自动创建字段功能,让我们可以不在模型中定义也能在数据库中自动创建,这个功能默认是关闭的,可以通过如下配置打开.
在config.xml
中添加如下配置:
<property name="orm.autoGeneateModelFields">false</property>
这样一来,在我们创建的Model类对应的数据库表中,就会有两个字段:
created_at
updated_at
其中created_at
字段是我们定义的createdAt
字段的映射,updated_at
字段就是leap自动创建的了,这个字段对应模型的updatedAt
属性,当然,实际上created_at
如果没有在模型中定义,也会自动创建。
当然有时候某些模型我们并不希望leap自动给我们添加这两个属性,那么可以采取如下两种方式来关闭leap的属性自动生成:
@AutoGenerateColumns
注解
如果某个Model的子类不希望自动生成createdAt
和updatedAt
字段,可以在这个类上添加注解@AutoGenerateColumns(false)
,这种方式只针对单独某个类不需要生成的,如果全局情况下不希望生成这两个字段,可以使用配置的方式.
注意:
- 配置了全局不自动生成字段后,即使对单独某个类注解了
@AutoGenerateColumns(true)
,也不会自动生成,此时建议自己在类中显式声明这两个属性.- 如果在应用启动之前,数据库的映射的物理表已经创建好了,并且该物理表没有这两个字段,那么也不会自动生成.
ORM默认映射规则
- 对于全小写的属性名,默认映射属性名和数据库字段名一致;
- 对于驼峰式命名的属性名,默认将属性名转化为下划线式,再与相同的数据库字段名一致.
注意:属性名强烈建议不要使用特殊字符.
Model对象的使用
现在我们已经创建好实体和映射关系了,接下来让我们看看如何使用leap的ActiveRecord.
我们先在UserModelController
中添加一个action:
public User create(String name,Integer age, String loginId, String password){
User user = new User();
user.setName(name);
user.setAge(age);
user.setLoginId(loginId);
user.setPassword(password);
try {
user.create();
return user;
} catch (RecordNotSavedException e) {
e.printStackTrace();
return null;
}
}
这个接口我们用来创建一个用户,现在我们启动应用,测试请求如下:
http请求:
uri:
/user_model/create
params:
name:张三
age:20
loginId:zhangsan
password:123123
返回结果:
{
"id": "990104a3-d47e-472d-86c0-5d6e13a02b4f",
"name": "张三",
"age": 20,
"password": "123123",
"createdAt": 1447125740861,
"loginId": "zhangsan"
}
这个结果告诉我们创建用户成功了.并且帮我们自动填充了id和创建时间.
现在我们回头看看,将用户插入数据库的操作,其实只有一句代码:
user.create();
这里我们根本不用关注如何获取datasource,也不用关注如何获取dao对象,每一个User的实例就对应一行数据库记录,并且记录自身可以直接操作数据库,非常简单方便. 另外,还有对应的几个接口:
user.update();//更新记录
user.delete();//删除记录
user.save();//根据user的id是否存在判断是更新还是插入记录
除了上面基本的操作之外,leap还提供了强大的记录查询功能,现在我们先在UserModelController
中添加另一个action:
public List<User> query(String name, Integer age, String loginId){
if(name == null && age == null && loginId == null){
return User.all();
}
if(name == null){
name = "";
}
if(loginId == null){
loginId = "";
}
CriteriaQuery<User> cq = User.<User>query();
cq.where("name like ? and age like ? and loginId like ?",
"%"+name+"%",age==null?"%%":"%"+age+"%","%"+loginId+"%");
return cq.list();
}
启动应用后发http请求如下:
http请求:
uri:
/user_model/query
params:
返回结果:
[
{
"id": "15583768-8b55-475f-87e0-6ddaee3cb910",
"name": "王五",
"age": 32,
"password": "123123",
"createdAt": 1447134433000,
"loginId": "wangwu"
},
{
"id": "8bcaa593-fddd-4c6f-8443-b5091cae4388",
"name": "赵六",
"age": 19,
"password": "123123",
"createdAt": 1447134462000,
"loginId": "zhaoliu"
},
{
"id": "990104a3-d47e-472d-86c0-5d6e13a02b4f",
"name": "张三",
"age": 20,
"password": "123123",
"createdAt": 1447125740000,
"loginId": "zhangsan"
},
{
"id": "d8e4be73-e9f8-4c70-bb85-cbe06d2341e5",
"name": "李四",
"age": 21,
"password": "123123",
"createdAt": 1447134405000,
"loginId": "lisi"
}
]
这里我们没有任何参数,返回的是所有数据,如果再添加一个name=张三
的参数,可以看到返回结果如下:
[
{
"id": "990104a3-d47e-472d-86c0-5d6e13a02b4f",
"name": "张三",
"age": 20,
"password": "123123",
"createdAt": 1447125740000,
"loginId": "zhangsan"
}
]
从控制台日志我们也可以看到执行的sql:
SQL : select t.* from leap_user t where name like ? and age like ? and login_id like ?
ARGS : ['%张三%','%%','%%']
通过代码我们可以看到,查询是通过Model的query()
接口生成的CriteriaQuery
对象来处理的,这个对象包含很多查询接口:
cq.pageResult(page);//设置分页
cq.delete();//批量删除
cq.count();//查询数量
除了上述例子,还有许多接口,可以查询api文档.