Skip to content

Reload SQL dynamically

zzg edited this page Mar 21, 2017 · 1 revision

使用前,请参考 这里 先安装插件

数据库定义

1: 假定数据库为本机: test, 用户名和密码均为: root

@DB(url="jdbc:mysql://localhost:3306/test?allowMultiQueries=true"
  ,username="root",password="root")
public interface TestDB {  
    //该定义方便后续直接执行查询
    public static DBConfig DB=DBConfig.fromClass(TestDB.class); 
}

2: 创建测试表和数据

执行下面的代码 或 使用数据库工具运行其中的SQL

public class Create {
    public static void main(String[] args) {  
        TestDB.DB.execute(""/**~{
            DROP TABLE IF EXISTS `user` ;
            DROP TABLE IF EXISTS `blog` ;
			
            CREATE TABLE IF NOT EXISTS `user` (
                `id`     int(11)     NOT NULL AUTO_INCREMENT COMMENT '用户ID',
                `name`   varchar(64) NOT NULL                COMMENT '用户名称',
                `passwd` varchar(64) DEFAULT '123456'        COMMENT '用户密码' ,
                `status` int(11)     DEFAULT 1               COMMENT '用户状态',
                PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '用户表';
			 
            CREATE TABLE IF NOT EXISTS  `blog` (
                `id`      int(11)      NOT NULL AUTO_INCREMENT COMMENT '博客ID',
                `user_id` int(11)      NOT NULL                COMMENT '用户ID',
                `title`   varchar(64)  NOT NULL                COMMENT '博客标题',
                `content` text     DEFAULT NULL                COMMENT '博客内容' ,
                `create_time` DATETIME NOT NULL                COMMENT '创建时间' ,
                `status` int(11)   DEFAULT NULL                COMMENT '博客状态',
                PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '博客表';
		 
            INSERT INTO `user`(id,name,passwd,status)VALUES(1,"zzg1","123",1);
            INSERT INTO `user`(id,name,passwd,status)VALUES(2,"zzg2","123",1);
            INSERT INTO `user`(id,name,passwd,status)VALUES(3,"zzg3","123",1);
			
            INSERT INTO `blog`(user_id,title,content,status,create_time)VALUES
                (1,'Hello-11','Test Content 11',1,now()),
                (1,'Hello-12','Test Content 12',1,now()),
                (1,'Hello-13','Test Content 13',1,now()),
                (1,'Hello-14','Test Content 14',0,now()),
                (1,'Hello-15','Test Content 15',0,now());
        }*/);
    }
}

数据访问类

数据访问类,建议该类只涉及数据库相关的一些操作, 不要引入复杂的业务逻辑。

public class UserBlogDao {
    final static long $VERSION$= 18L; //!!! 版本号, 每次保存为自动 +1
	
    //@Select 注解指示该方法需自动生成结果类
    //默认类名: Result + 方法名, 默认包名:数据访问类的包名+"."+数据访问类的名称(小写)
    //可选参数:name 指定生成结果类的名称
    @Select(name="test.result.UserBlogs") 
	
    //!!! 保存后会自动修改该函数的返回值为: List -> List<UserBlogs>
    //第一次编写时,由于结果类还不存在, 为了保证能够编译正常,
    //函数的返回值 和 查询结果要用 泛值 替代, 保存后,插件会自动修改.
    //函数的返回值 和 查询结果 泛值的对应关系分三类如下:
    //1. List查询
    //public DataTable   method_name(...){... return Query.getList();   }    或
    //public List        method_name(...){... return Query.getList();   }    
    //
    //2. Page查询
    //public Page   method_name(...){... return Query.Page();      }
    //
    //3. 单条记录
    //public Object method_name(...){... return Query.getResult(); }
    //
    public List<UserBlogs>  selectUserBlogs(int user_id){ 
        Query q=TestDB.DB.createQuery();
		           
        q.add(""/**~{
            SELECT a.id,a.name,b.title, b.content,b.create_time
                FROM user a, blog b   
                WHERE a.id=b.user_id AND a.id=?
        }*/, user_id);
		 
        //!!! 保存后会自动修改 查询结果为: getList() -> getList<UserBlogs.class>
        return q.getList(UserBlogs.class); 
    } 
	
	
    @Select //自动产生结果类: test.dao.userblogdao.ResultSelectUserBlogsOne
    public ResultSelectUserBlogsOne  selectUserBlogsOne(int user_id){ 
        Query q=TestDB.DB.createQuery();
		           
        q.add(""/**~{
            SELECT a.id,a.name,b.title, b.content,b.create_time
                FROM user a, blog b   
                WHERE a.id=b.user_id AND a.id=?
            }*/, user_id);
		 
            return q.getResult(ResultSelectUserBlogsOne.class);
    } 
	
    @Tx  //在一个事务中运行这个方法
    public void  updateUserBlog(int user_id,int blog_id){
        User user=User.SELECT().selectByPrimaryKey(user_id);
        Blog blog=Blog.SELECT().selectByPrimaryKey(blog_id);
		
        user.setName("new name").update();
        blog.setContent("new content").update();
    }
	
    //使用Map存储查询结果
    public Page<DataMap>  selectUserBlogsAsListMap(int user_id,int limit,int offset){
        Query q=TestDB.DB.createQuery();
		         
        q.add(""/**~{
            SELECT a.id,a.name,b.title,b.content,b.create_time
                FROM user a, blog b 
                WHERE a.id=b.user_id AND a.id=?		
        }*/, user_id);
		 
        return q.getPage(limit,offset);
    }
}

调用示例

public class UserAction {
    public static void main(String[] args)throws Exception {
        UserAction action=new UserAction();
        action.findUserBlogs(1);
    }
	
    /**
     * 这里需要调用 Query.Create()来创建数据访问类. 
     * 不能直接调用new, 否则会丢失@Tx标记方法的事务特性, 以及数据访问类更新加载的特性
     */
    private UserBlogDao dao=Query.create(UserBlogDao.class);
	
    public void findUserBlogs(int user_id){
        for(UserBlogs x: dao.selectUserBlogs(user_id)){
            System.out.println(x.getContent());
        }
    }
}

动态更新-数据访问类

将修改后的 UserBlogDao.java 文件(可包含子目录) 放置到目录 ./monalisa/agent即可实现在运行时更新数据访问接口.

受限于JVM的代码热修改, 动态更新的数据访问类有如下一些限制

  • 不能增加或修改方法的签名
  • 不能增加或修改类的成员变量 (但方法中的变量和代码可以任意修改)

数据访问类的更新判断标准为:

  • 如果类包含有版本号字段: $VERSION$, 则加载最高的版本号作为运行的类
  • 否则比较运行期的.class和agent/*.java文件的更新日期, 加载最新的日期作为运行的类

类更新时会输入如下日志信息:

Reload classes(1), Class-Path: ./target/monalisa/_java
*******************************************************************************
<Replace> class: test.dao.UserBlogDao, version: 1 -> 2, timestamp: xxx-> yyy
*******************************************************************************

例子工程