【ActiveJdbc】02
一、基本的数据库操作
数据模型层:
import org.javalite.activejdbc.Model;
数据访问层:
import org.javalite.activejdbc.Base; import org.javalite.activejdbc.DB;
基础的查询方法:
@Test public void commonQuery() { // 数据集读取 // List<Map> usersList = Base.findAll("select * from users where company_id = ? ", companyId); // for(Map record: userList){ // System.out.println("first_name: " + record.get("first_name")); // System.out.println("last_name: " + record.get("last_name")); // } // 单个值的读取 // Long lastLoginTime = Convert.toLong(Base.firstCell("select time from logins where user_id ? order by created_at limit 1", 123)); // 单个列读取 // List ssns = Base.firstColumn("select ssn from people where first_name = ?", "John"); // ssns.forEach(System.out::println); }
二、Setter & Getter
ActiveJdbc提供的读写方式:
@Test public void commonQuery() { // 读方法 Employee employee = Employee.findById(2); Object columnName = employee.get("columnName"); String colName = columnName.toString(); // 写方法 Employee employee1 = new Employee(); employee1.set("colName1", "value1"); employee1.set("colName2", "value2"); employee1.set("colName3", "value3"); employee1.save(); }
没有JavaBean方式的?作者的意思是你自己重写就行了
package cn.cloud9.entity; import org.javalite.activejdbc.Model; import org.javalite.activejdbc.annotations.Table; @Table("people") public class Employee extends Model { public void setFirstName(String firstName){ set("first_name", firstName); } public String getFirstName(){ return getString("first_name"); } }
类型转换问题
作者提供了一些类型可以直接获取的方法:
这里我就列举四种常用的:
employee.getString("columnName");
employee.getInteger("columnName");
employee.getDate("asdasd");
employee.getBigDecimal("asdasd");
三、数据库表和模型名称的对应
一般来说ActiveJdbc默认将类的名字全大写识别成表名称
如果表名含有特殊符号不能直接识别,可以加上@Table注解声明
package cn.cloud9.entity; import org.javalite.activejdbc.Model; import org.javalite.activejdbc.annotations.Table; @Table("people") public class Employee extends Model { }
四、连接管理
线程连接传播
ActiveJDBC 模型在运行时利用在当前线程上找到的连接。
在任何 DB 操作之前,此连接由 Base 或 DB 类放在本地线程上。
这种方法允许更简洁的 API,不需要像其他 Java ORM 那样需要 DB Session 或持久管理器。
这是一个简单的程序:
public static void main(String[] args) { Base.open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "the_user", "the_password"); Employee.findAll().dump(); Base.close(); }
在第 2 行,Base 类将打开一个新连接并将其附加到当前线程。此连接也将标有名称default
。
在第 3 行,从线程中查找连接并由模型使用(并将结果转储到 STDIO)
在第 4 行,连接关闭并从线程中清除。
Base用于单个库的连接访问,而DB支持多个库多个服务器实例的访问
ActiveJDBC 有一个逻辑数据库
的概念。但是,一个应用程序可以同时连接到多个数据库。
在这种情况下,ActiveJDBC 允许为不同的数据库分配不同的逻辑名称。
例如,一个人可能有一个包含会计数据的 Oracle 数据库和一个包含库存控制数据的 MySQL 数据库。
在这种情况下,您可能希望将会计
数据库和库存
数据库作为分配给这些数据库的逻辑名称。
打开和关闭连接是通过类 Base 或 DB 完成的。DB 类用于系统中有多个数据库的情况,例如会计
和库存
。
例子:
new DB("inventory").open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "dbuser", "...");
在此代码示例中,打开了一个数据库连接,并连接到名称为inventory
的本地线程。
类 Base 和 DB 相互镜像,具有完全相同的 API,除了:
- DB 上的所有方法都是实例方法,而 Base 类上的所有方法都是静态方法。
- 类 DB 构造函数接受 DB 名称,而 Base 始终使用 DB 名称操作:
默认
这意味着这些行是等效的:
new DB("default").open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "root", "p@ssw0rd");
和:
Base.open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "root", "p@ssw0rd");
Base
如果系统中只有一个数据库,请使用class,否则使用DB
测试:
@Test public void commonQuery() { new DB("default") .open( "com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3308/atguigu-syt?serverTimeZone=Asia/Shanghai", "root", "123456" ); Employee employee = Employee.findById(1); System.out.println(employee); }
多个库的模型设置:
与多个数据库关联的模型
ActiveJDBC 允许在表示来自不同数据库的表的应用程序中混合使用模型。
默认情况下,模型属于数据库默认值
,但模型与数据库的关联可以用注释覆盖@DbName
:
package cn.cloud9.entity; import org.javalite.activejdbc.Model; import org.javalite.activejdbc.annotations.DbName; import org.javalite.activejdbc.annotations.Table; @DbName("atguigu-syt") @Table("people") public class Employee extends Model { }
就是需要标注这个模型是对应哪个库下的哪张表
作者提供的多库操作案例:
多数据库示例 请参阅此处的来源:multimple-db-example。 对于这个例子,我们将有两个模型,一个代表 Oracle 数据库中的表,另一个代表 MySQL 这两个模型定义如下: @DbName("corporation") public class Employee extends Model {} 和: @DbName("university") public class Student extends Model {} 主类如下所示: public class Main { public static void main(String[] args) { new DB("corporation").open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "root", "p@ssw0rd"); new DB("university").open("oracle.jdbc.driver.OracleDriver", "jdbc:oracle:thin:@localhost:1521:xe", "activejdbc", "activejdbc"); Employee.deleteAll(); Student.deleteAll(); Employee.createIt("first_name", "John", "last_name", "Doe"); Employee.createIt("first_name", "Jane", "last_name", "Smith"); Student.createIt("first_name", "Mike", "last_name", "Myers"); Student.createIt("first_name", "Steven", "last_name", "Spielberg"); System.out.println("*** Employees ***"); Employee.findAll().dump(); System.out.println("*** Students ***"); Student.findAll().dump(); new DB("corporation").close(); new DB("university").close(); } } // 在这个应用程序开始时,两个命名连接被打开,然后我们继续使用与这些连接关联的模型。在应用程序结束时,两个命名连接被关闭。类 DB 是轻量级的,可以不保留对它的引用,而是每次都创建一个新实例。如果你确实想保留一个引用,那也没什么坏处。
五、连接池操作:
和Tomcat的JNDI结合操作:
ActiveJDBC 接受到现有池的 JNDI 连接 URL。
它提供了一些DB.open()
和Base.open()
方法,以开放游泳池的连接。
如果使用采用标准 JDBC 参数的方法版本,则不使用池。
这只是一种打开全新连接的便捷方法,例如:
Base.open("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "root", "pwd");
但是,如果使用此调用:
Base.open("java:comp/env/jdbc/testdb");
然后它将使用 JDNI 名称从池中查找连接。
通常这是从容器内调用的,名称指向在容器级别配置的池化 JNDI 数据源。
如果不是JNDI提供的连接池,则提供DataSource数据源来入参
如果你想直接使用某个连接池,你可以通过向 Base/DB 类提供数据源来实现:
new DB("default").open(datasourceInstance); //or: Base.open(datasourceInstance);
配置文件与多个环境:
为不同环境配置多个连接的最简单方法是使用属性文件。
按照惯例,这个文件被调用database.properties
并位于类路径的根目录。
以下是此类文件的示例:
development.driver=com.mysql.jdbc.Driver development.username=user1 development.password=pwd development.url=jdbc:mysql://localhost/acme_development test.driver=com.mysql.jdbc.Driver test.username=user2 test.password=pwd test.url=jdbc:mysql://localhost/acme_test production.jndi=java:comp/env/jdbc/acme
为了使其工作,您需要将环境变量配置ACTIVE_ENV
为等于属性集键的值。
根据上面的文件,ACTIVE_ENV
可以采用值development
和production
. 这test
是特殊的,因为它用于开发环境,但用于运行测试(测试模式)。
配置文件并放置在类路径的根目录后,您将使用无参数方法打开连接,如下所示:
org.javalite.activejdbc.connection_config.DBConfiguration.loadConfiguration("/database.properties); // The previous line is necessary starting with version 2.3.2-j8. new DB("default").open(); //or: Base.open();
第一行只需要在开始时调用一次即可从文件加载配置。
将选择与当前环境相关的配置并用于打开连接。
这使得开发存在于不同环境中的应用程序变得容易,并且只需知道
在每个环境中连接的位置。
如果
ACTIVE_ENV
未定义环境变量,则框架默认为 environmentdevelopment
。
插入记录的方式:
这是常规插入的操作步骤:
Person p = new Person(); p.set("first_name", "John"); p.set("last_name", "Doe"); p.set("dob", "1935-12-06"); p.saveIt();
使用方法链来操作:
Person p = new Person(); p.set("name", "John") .set("last_name", "Doe") .set("dob", "1935-12-06") .saveIt();
使用重载的可变参数注入:
Person p = new Person(); p.set( "first_name", "Sam", "last_name", "Margulis", "dob", "2001-01-07" ); p.saveIt();
支持数组形式的批处理:
String[] names = {"first_name", "last_name", "dob"}; Object[] values = {"John", "Doe", dob} new Person().set(names, values).saveIt();
要求Key数组和Value数组的长度一致
直接从Map填充值:
作者还贴心的提供了一个fromMap方法,
Map可以理解为从请求中封装好的装填数据
Map values = ... initialize map Person p = new Person(); p.fromMap(values); p.saveIt();
save 和 saveIt方法的区别?
ActiveJDBC 类 Model 提供了两种保存实体的方法:save()
和saveIt()
. 这两种方法都将在保存期间涉及验证,
但在方法 save() 的情况下,将静默退出而不抛出异常。
如果验证失败,该实例将附加一个错误集合。这在 Web 应用程序的上下文中非常有用。下面是一个例子:
Person person = new Person(); person.fromMap(requestParams); if(person.save()) //<<<=== will not throw exception and will not save in case there are validation errors. //show page success else{ request.setAttribute("errors", person.errors()); //show errors page, or same page so that user can correct errors. }
saveIt()
如果出现验证问题,该方法将抛出异常。
该save()
方法在 Web 应用程序的上下文中更有意义,而saveIt()
在非 Web 应用程序情况下更有用 - 批量插入、测试等。
总结一下,作者意思推荐使用saveIt去处理插入,因为会抛出异常信息,对批量处理操作更好
或者直接调用Create & CreateIt
类 Model 还提供了两种创建记录的便捷方法:create()
和createIt()
.
这两种方法在语义上是有区别的,和save()
和saveIt()
方法之间是一样的,只是在这种情况下,ActiveJDBC 一步创建并尝试保存对象。
Person p = Person.create("first_name", "Sam", "last_name", "Margulis", "dob", "2001-01-07");
p.saveIt();
或者:
Person p = Person.createIt("first_name", "Sam", "last_name", "Margulis", "dob", "2001-01-07");
该create()
和createIt()
方法接受的参数,其中名称交错值的列表。
这类似于上面描述的 varargs setter,但也包括save()
和saveIt()
方法的语义。