历时一年开发,Java持久层工具jSqlBox第一个正式版本发布

历时一年开发,Java持久层工具jSqlBox终于推出第一个正式版本1.0.0版。详见项目地址:https://gitee.com/drinkjava2/jSqlBox, 以下为项目内容简介:

jSqlBox是一个Java持久层工具,设计目标是用来代替目前的Hibernate/MyBatis/JdbcTemplate/DbUtils等持久层工具。jSqlBox的主要特点:

模块化设计。 jSqlBox将Jdbc工具、事务、方言和数据库脚本生成等功能分成子模块,由不同的子项目实现,每个子项目都可以脱离jSqlBox存在,甚至可供其它ORM工具使用,避免重复发明轮子。同时jSqlBox的各个子模块(除TinyNet外)在开发中大量借签了其它成熟项目,例如,方言模块(jDialects)抽取了Hibernate的70种方言,事务模块(jTranscations)在提供简化版声明式事务的同时兼容Spring事务,底层数据库访问工具(jDbPro)基于Apache Commons DbUtils(以下简称DbUtils),链式风格和模板的引入则分别受jFinal和BeetlSql启发。
与DbUtils兼容,基于DbUtils开发的项目可以无缝升级到jSqlBox,享受到jSqlBox提供的高级功能如跨数据库开发、分页、模板、JPA注解支持、数据库脚本生成、ActiveRecord风格等。
支持SQL写法多。得益于模块式架构,jSqlBox从最底层的JDBC到最新式的NoSQL式查询都有,是目前支持SQL写法最多的持久层工具。
学习曲线平滑、可维护性好。得益于模块化架构,jSqlBox项目的本体部分仅由13个Java类组成。持久层的工作被分散到各个子模块中去了,模块化设计带来的好处就是模块化学习,只要了解各个模块的功能,就能很快掌握jSqlBox的使用。最极端情况是DbUtils使用者可以无学习直接上手jSqlBox.
多项技术创新,如Inline风格、动态配置、改进的链式风格、NoSQL自动越级查询及树结构查询等。

jSqlBox的开发目的是试图解决其它持久层的一些问题,主要有:
架构有问题,重复发明轮子。其它持久层通常试图提供一篮子解决方案,但是不分模块的开发方式使得代码不可复用。例如Hibernate、MyBatis、jFinal、NutzDao、BeetlSql、JdbcTemplate等工具,都面临着如何处理跨数据库开发的问题,它们的做法要么就是自已从头到尾开始开发一套方言工具,要么就是干脆不支持数据库方言,没有借签其它工具的成果,也没有考虑将自己的成果分享给其它工具。
过于偏执于某项技术,试图用一把锤子解决所有问题。例如Hibernate和JPA对象化设计过度,臃肿复杂。MyBatis的XML配置繁琐,没有提供CRUD方法(有插件改善,但总不如原生支持好)。JdbcTemplate和DbUtils偏重于SQL,开发效率低。BeetlSql完全围绕模板打转。jFinal和Nutz则对捆绑式推销情有独钟,没有将持久层独立出来,买个萝卜搭块糕。
不支持动态配置。从数据库表到Java对象,称为ORM,这需要进行配置。但这些常见的持久层工具,不支持动态配置,这对于需要动态生成或修改配置的场合是个重大缺陷。例如JPA注解或XML配置的实体Bean,在运行期很难更改外键关联关系、数据库表映射关系配置。

jSqlBox的缺点

它太新了,诞生不到一个月(虽然陆陆续续在Github和码云上的开发时间已超过1年),使用者少,设计缺陷和程序中的Bug都没有暴露出来,文档不完善,目前不建议在重要项目使用。
如何在项目中引入jSqlBox?

jSqlBox需Java6或以上,在项目pom.xml中添加如下行:

<dependency>
<groupId>com.github.drinkjava2</groupId>
<artifactId>jsqlbox</artifactId>
<version>1.0.0</version>
</dependency>

Maven将自动下载jsqlbox-1.0.0.jar、jdbpro-1.7.0.1.jar、jdialects-1.0.6.jar、commons-dbutils-1.7.jar四个包。下图显示了各个包之间的依赖关系及包的大小以及源文件数量。
[img]http://dl2.iteye.com/upload/attachment/0128/0373/89914880-f377-3998-88e0-103ab95a912f.png[/img]
如需要配置事务,还需添加对jtransactions-1.0.0.jar的依赖,具体请参见jTransactions项目。
jSqlBox快速入门
第一个jSqlBox示例: 数据源设定、上下文生成、创建数据库表格、插入和读取内容

public class HelloWorld {
@Id
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}

@Test
public void doText() {
HikariDataSource ds = new HikariDataSource();//连接池设定
ds.setJdbcUrl("jdbc:h2:mem:DBName;MODE=MYSQL;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=0");
ds.setDriverClassName("org.h2.Driver");//利用H2内存数据库来进行单元测试
ds.setUsername("sa");
ds.setPassword("");

SqlBoxContext ctx = new SqlBoxContext(ds); //jSqlBox上下文生成
ctx.setAllowShowSQL(true);//打开jSqlBox的日志输出
String[] ddls = ctx.getDialect().toCreateDDL(HelloWorld.class);//数据库DDL脚本生成
for (String ddl : ddls)
ctx.nExecute(ddl);//新建数据库表

HelloWorld hello = new HelloWorld();
hello.setName("Demo");
ctx.insert(hello);//插入一行记录
Assert.assertEquals("Demo", ctx.nQueryForObject("select name from helloworld"));
ds.close();//关闭连接池
}
}

运行一下单元测试,没有报错,说明数据库已成功插入了一行数据。 以上示例利用了jDialects的功能创建了数据库脚本并在jSqlBox中运行并创建了数据库表。jDialects支持15个主要的JPA注解,有9种主键生成方式,详情请见jDialects项目。在实际开发环境中,往往用Spring或jBeanBox之类的IOC工具配置连接池单例和SqlBoxContext单例,例如:

SqlBoxContext ctx = BeanBox.getBean(CtxBox.class);//由IOC工具来获取SqlBoxContext单例

利用IOC工具对各种常见数据库进行连接池的配置可详见单元测试的DataSourceConfig.java示例,因为我是jBeanBox的作者,所以用jBeanBox来演示,如果换成用Spring来配置也是一样的。
分页


jSqlBox利用了jDialects进行跨数据库的(物理)分页,在出现SQL的地方,以下几种写法都可实现分页查询:

ctx.nQuery(new MapListHandler(), ctx.paginate(3, 5, "select * from users"));
ctx.nQuery(new MapListHandler(), SqlBoxContext.pagin(3, 5) + "select * from users");
ctx.nQuery(new MapListHandler(), "select * from users" + pagin(3,5));//静态引入方法

动态配置

User u=new User();
u.tableModel().setTableName("user_tb2");//动态改变映射数据表名为"user_tb2"
u.tableModel().fkey().columns("teamId").refs("team", "id");//动态给teamId字段添加外键约束
u.columnModel("id").pkey();//动态设定数据库表中的id列为主键
u.box().setContext(ctx2);//动态切换到新的上下文
u.box().
...

以上示例中User继承于ActiveRecord类,拥有box()等方法,可以在运行期动态访问并改变User类对应的TableModel配置(见jDialects项目)。
事务

jSqlBox项目本身不提供事务服务,而是利用jTransactions项目进行事务管理,这是一个包含了TinyTx和SpringTx两个事务实现的独立的事务服务工具。如果是从DbUtils移植过来的旧项目,也可保持原来的事务运作方式,因为jSqlBox与DbUtils兼容。
重头戏 - jSqlBox支持的10种SQL写法:

SQL写法1 - DbUtils对JDBC的包装,手工负责连接的获取和关闭,获取连接用prepareConnection()方法,关闭连接用close(conn)方法。
因为SqlBoxContext是DbUtils的QueryRunner类的子类,所以可以直接使用DbUtils的所有方法。

Connection conn = null;
try {
conn = ctx.prepareConnection();
ctx.execute(conn, "insert into users (name,address) values(?,?)", "Sam", "Canada");
ctx.execute(conn, "update users set name=?, address=?", "Tom", "China");
Assert.assertEquals(1L,
ctx.queryForObject(conn, "select count(*) from users where name=? and address=?", "Tom", "China"));
ctx.execute(conn, "delete from users where name=? or address=?", "Tom", "China");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
ctx.close(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}

SQL写法2 - DbUtils对JDBC的包装之二,无需关心连接的获取和关闭,使用这种方式要注意SqlBoxContext的生成必须提供一个dataSource构造参数。

try {
ctx.execute("insert into users (name,address) values(?,?)", "Sam", "Canada");
ctx.execute("update users set name=?, address=?", "Tom", "China");
Assert.assertEquals(1L,
ctx.queryForObject("select count(*) from users where name=? and address=?", "Tom", "China"));
ctx.execute("delete from users where name=? or address=?", "Tom", "China");
} catch (SQLException e) {
e.printStackTrace();
}

SQL写法3 - nXxxx()方法,从jDbPro项目开始新加入的方法,不用再捕捉SqlException异常。SqlException转化为运行时异常抛出,这种方式通常与支持声明式事务的AOP工具如Spring联用(下同)。

ctx.nExecute("insert into users (name,address) values(?,?)", "Sam", "Canada");
ctx.nExecute("update users set name=?, address=?", "Tom", "China");
Assert.assertEquals(1L,
ctx.nQueryForObject("select count(*) from users where name=? and address=?", "Tom", "China"));
ctx.nExecute("delete from users where name=? or address=?", "Tom", "China");

SQL写法4 - iXxxx()方法,Inline风格,在SQL里直接写参数,参数暂存在Threadlocal中,SQL执行时自动转化为preparedStatement,这种方式的优点是被赋值的字段和实际参数可以写在同一行上,字段多时利于维护,也方便根据不确定的条件动态拼接SQL。这是始于jSqlBox的技术创新。

ctx.iExecute("insert into users (", //
" name ,", param0("Sam"), //SqlBoxContext.param0()方法的静态引入,下同
" address ", param("Canada"), //
") ", valuesQuesions());
param0("Tom", "China");
ctx.iExecute("update users set name=?,address=?");
Assert.assertEquals(1L, ctx
.iQueryForObject("select count(*) from users where name=? and address=?" + param0("Tom", "China")));
ctx.iExecute("delete from users where name=", question0("Tom"), " or address=", question("China"));

SQL写法5 - tXxxx()方法, 模板风格,利用模板来存放SQL变量:

Map<String, Object> params=new HashMap<String, Object>();
User sam = new User("Sam", "Canada");
User tom = new User("Tom", "China");
params.put("user", sam);
ctx.tExecute(params,"insert into users (name, address) values(#{user.name},#{user.address})");
params.put("user", tom);
ctx.tExecute(params,"update users set name=#{user.name}, address=#{user.address}");
params.clear();
params.put("name", "Tom");
params.put("addr", "China");
Assert.assertEquals(1L,
ctx.tQueryForObject(params,"select count(*) from users where name=#{name} and address=#{addr}"));
params.put("u", tom);
ctx.tExecute(params, "delete from users where name=#{u.name} or address=#{u.address}");

SQL写法6 - tXxxx()方法,模板风格和Inline风格的结合使用:

user = new User("Sam", "Canada");
put0("user", user);
ctx.tExecute("insert into users (name, address) values(#{user.name},#{user.address})");
user.setAddress("China");
ctx.tExecute("update users set name=#{user.name}, address=#{user.address}" + put0("user", user));
Assert.assertEquals(1L,
ctx.tQueryForObject("select count(*) from users where ${col}=#{name} and address=#{addr}",
put0("name", "Sam"), put("addr", "China"), replace("col", "name")));
ctx.tExecute("delete from users where name=#{u.name} or address=#{u.address}", put0("u", user));

SQL写法7 - tXxxx()方法,依然是模板风格,但是运行期切换成使用另一个模板"NamedParamSqlTemplate",采用冒号来作参数分界符。
jSqlBox是一种开放式架构设计,它对事务、日志、模板都有缺省实现,但是也支持切换成其它第三方实现。

user = new User("Sam", "Canada");
ctx.setSqlTemplateEngine(NamedParamSqlTemplate.instance());
put0("user", user);
ctx.tExecute("insert into users (name, address) values(:user.name,:user.address)");
user.setAddress("China");
ctx.tExecute("update users set name=:user.name, address=:user.address" + put0("user", user));
Assert.assertEquals(1L, ctx.tQueryForObject("select count(*) from users where ${col}=:name and address=:addr",
put0("name", "Sam"), put("addr", "China"), replace("col", "name")));
ctx.tExecute("delete from users where name=:u.name or address=:u.address", put0("u", user));

SQL写法8 - Data Mapper数据映射风格,与Hibernate中的写法一样。从性能方面考虑,jSqlBox强烈建议实体类继承于ActiveRecord类,但是对于纯粹的POJO类,jSqlBox也是支持的:

User user = new User("Sam", "Canada");
ctx.insert(user);
user.setAddress("China");
ctx.update(user);
User user2 = ctx.load(User.class, "Sam");
ctx.delete(user2);

SQL写法9 - ActiveRecord风格,继承于ActiveRecord类的实体类,自动拥有insert/update/delete等CRUD方法和一个box()方法可用来在运行期动态更改配置。jSqlBox项目命名的由来即因为这个box()方法。

System.out.println("=== ActiveRecord style ===");
user = new User("Sam", "Canada");
user.box().setContext(ctx);// set a SqlBoxContext to entity
user.insert();
user.setAddress("China");
user.update();
user2 = user.load("Sam");
user2.delete();

以下是ActiveRecord风格的一个变种,使用这种写法的前提是必须在程序启动时调用SqlBoxContext.setDefaultContext(ctx)方法设定一个全局缺省上下文,适用于单数据源场合。这种写法的优点是业务方法里完全看不到持久层工具的影子:

user = new User("Sam", "Canada").insert();
user.setAddress("China");
user.update();
user2 = user.load("Sam");
user2.delete();

SQL写法10 - 链式风格,严格来说这种风格还是应该归类于ActiveRecord模式。

链式风格之一:
new User().put("id","u1").put("userName","user1").insert();

链式风格之二:
new Address().put("id","a1","addressName","address1","userId","u1").insert();

链式风格之三:
new Email().putFields("id","emailName","userId");
new Email().putValues("e1","email1","u1").insert();
new Email().putValues("e2","email2","u1").insert();

注:如果有大批的插入、更新语句,可以在开始前调用ctx.nBatchBegin()方法,结束后调用ctx.nBatchEnd方法,则插入方法将自动汇总成批量操作,不过使用此功能的前提是数据库要支持批量操作功能(如MySQL要设定rewriteBatchedStatements=true)。
NoSQL查询

下面要介绍的是jSqlBox项目中的NoSql式查询方法,对于下图的一组关系数据库表格,User与Role是多对多关系,Role与Privilege是多对多关系,UserRole和RolePrivilege是两个中间表,用来连接多对多关系。如果用普通的SQL进行多表关联查询比较麻烦,jSqlBox中包含了一个TinyNet子项目,专门用于将关系数据库转化为内存中的图结构,从而可以运用NoSql式查询方法,在节点间浏览查询。

[img]http://dl2.iteye.com/upload/attachment/0128/0375/8bd647ab-b402-3c78-a39b-256c31130e8f.png[/img]


NoSql查询示例1 一 查找u1和u2两个用户所具有的权限

TinyNet net = ctx.netLoad(new User(), new Role(), Privilege.class, UserRole.class, RolePrivilege.class);
Set<Privilege> privileges = net.findEntitySet(Privilege.class,
new Path("S-", User.class).where("id=? or id=?","u1","u2").nextPath("C-", UserRole.class, "userId")
.nextPath("P-", Role.class, "rid").nextPath("C-", RolePrivilege.class, "rid")
.nextPath("P+", Privilege.class, "pid"));
for (Privilege privilege : privileges)
System.out.print(privilege.getId()+" ");

输出结果:p1 p2 p3

说明:

netLoad方法将根据所给参数,调入所在表的所有内容,在内存中拼成TinyNet实例代表的图状结构。注意TinyNet不是线程安全类,在多线程环境下使用要小心。
与netLoad方法类似的一个方法是netLoadSketch方法,不是将全表加载,而是只加载所有主键和外键字段,这种懒加载可以提高性能并节约内存。
Path用于定义一个查找路径,构造器的第一个参数由两个字母组成,首字母S表示查找当前节点,P表示查找父节点,C表示查找子节点。第二个字母"-"表示查找的中间结果不放入查询结果,"+"则放入查询结果,"*"表示递归当前路径查找所有节点并放入查询结果。第二个参数定义目标对象类、类实例或数据库表名,第三个参数为可变参数,为两个目标之间的关联属性或关联列(即单外键或复合外键的列名),例如User和UserRole之间的关联字段为"userId"。
Path的where()方法定义一个表达式,用于判断节点是否可以选中,表达式支持的关键字有: 当前实体的所有字段名、>、 <、 =、 >、=、 <=、 +、 -、 *、/、null、()、not 以及 equals、contains等常见字符串函数,详见手册或单元测试源码。
Path的nextPath()方法用于定义下一个查找路径。

NoSql查询示例2 一 自动越级查询。示例1的写法比较啰嗦,下面是等价写法,用autoPath方法可以自动计算出搜索路径,从而实现越级查询:

TinyNet net = ctx.netLoad(new User(), new Role(), Privilege.class, UserRole.class, RolePrivilege.class);
Set<Privilege> privileges = net.findEntitySet(Privilege.class,
new Path(User.class).where("id='u1' or id='u2'").autoPath(Privilege.class));
for (Privilege privilege : privileges)
System.out.print(privilege.getId()+" ");

注意autoPath方法仅适用于查找路径唯一的情况,如果从User到Privilege有多个不同路径通达,就不能应用autoPath方法,而必须写出全路径。

NoSql查询示例3 一 Java原生方法判断。用Java原生方法进行节点的判断,可以利用熟悉的Java语言而且支持实体字段的重构。以下示例查询Email "e1"和"e5"所关联的权限:

@Test
public void testAutoPath2() {
insertDemoData();
TinyNet net = ctx.netLoad(new Email(), new User(), new Role(), Privilege.class, UserRole.class,
RolePrivilege.class);
Set<Privilege> privileges = net.findEntitySet(Privilege.class,
new Path(Email.class).setValidator(new EmailValidator()).autoPath(Privilege.class));
for (Privilege privilege : privileges)
System.out.println(privilege.getId());
Assert.assertEquals(1, privileges.size());
}

public static class EmailValidator extends DefaultNodeValidator {
@Override
public boolean validateBean(Object entity) {
Email e = (Email) entity;
return ("e1".equals(e.getId()) || "e5".equals(e.getId())) ? true : false;
}
}

NoSql示例4 手工加载。 TinyNet的加载也可以手工按需加载,而不是用netLoad方法全表调入,例如下表运行两个SQL,分两次查询数据库并将结果合并成一个TinyNet实例:

List<Map<String, Object>> mapList1 = ctx.nQuery(new MapListHandler(netProcessor(User.class, Address.class)),
"select u.**, a.** from usertb u, addresstb a where a.userId=u.id");
TinyNet net = ctx.netCreate(mapList1);
Assert.assertEquals(10, net.size());

List<Map<String, Object>> mapList2 = ctx.nQuery(new MapListHandler(),
netConfig(Email.class) + "select e.** from emailtb as e");
ctx.netJoinList(net, mapList2);
Assert.assertEquals(15, net.size());

上例中两个星号这种写法是jSqlBox项目中唯一打破标准SQL写法的地方,它表示查询User对象所有非transient属性所对应的数据库表字段。

NoSql查询示例5 一 树结构的查询,对于Adjacency List模式存储的树结构数据表,利用NoSQL查询非常方便,例如如下数据库表格:

[img]http://dl2.iteye.com/upload/attachment/0128/0377/abbb522a-4d3f-3b95-a67b-663f16b813d6.png[/img]

查询B节点和D节点的所有子节点(含B和D节点本身):

TinyNet net = ctx.netLoad(TreeNode.class);
Set<TreeNode> TreeNodes = net.findEntitySet(TreeNode.class,
new Path("S+", TreeNode.class).where("id=? or id=?", "B", "D").nextPath("C*", TreeNode.class, "pid"));
for (TreeNode node : TreeNodes)
System.out.print(node.getId() + " ");
输出: B D E F H I J K L

查询F节点和K节点的所有父节点(不含F和K节点本身):

TinyNet net = ctx.netLoad(TreeNode.class);
Set<TreeNode> TreeNodes = net.findEntitySet(TreeNode.class,
new Path("S-", TreeNode.class).where("id='F' or id='K'").nextPath("P*", TreeNode.class, "pid"));
for (TreeNode node : TreeNodes)
System.out.print(node.getId() + " ");
输出: B H A D

对于海量数据的大树,用netLoad方法调入全表到内存再用NoSQL方法查询是不现实的,这时可利用数据库的递归查询功能将一棵子树查找到内存中再用NoSQL方法查询。如果数据库不支持递归或有性能问题,也可参照本人发明的无限深度树方案的第三种方案(见http://drinkjava2.iteye.com/blog/2353983 ),可以仅用一条SQL语句查询出所需要的子树调入内存中。

对了,最后说一下,可能有人已经注意到,在上面的示例中,什么一对多,多对多之类其它ORM工具必不可少的配置在哪里? 答案是:还记得一开始动态配置的示例演示中,有这么一句么: u.tableModel().fkey().columns("teamId").refs("team", "id"); 这就是多对一配置了,还有一个FKey注解也是干同样的事,表示给user数据库表的teamId列添加数据库外键,参考team表的id主键字段。外键就是关系,这是为什么关系数据库被称为关系数据库,关系数据库只存在"多对一"这种唯一的关联,所以jSqlBox中没有什么多对多、一对多的配置,完全是基于传统的ER建模。jSqlBox支持复合主键和复合外键(详见jDialects项目)。程序员在运行期可以在程序里自由地添加或删除实体的外键约束配置(每个实体的配置都是固定配置的副本,只要不调用toCreateDDL方法生成脚本并执行,对实际数据库是没影响的),从而可以自由地创建对象间的关联,然后就可以用NoSQL方式查询了。另外说一下:NoSql查询是在不同类型的节点间浏览,所以返回结果也可以是不同的实体类,因篇幅原因就不在这里演示了。

以上即为jSqlBox的初步介绍,欢迎大家试用和挑错,同时也欢迎有兴趣者加入开发组。PDF使用手册正在制作中。目前如有兴趣试用的话,可参考本文介绍和单元测试源码来了解本项目。
附录 - 性能测试。

以下是jSqlBox不同SQL写法进行循环10000次CRUD操作的性能测试(单元测试源码SpeedTest.java),在H2内存数据库上跑(i3 2.4G),可以排除磁盘读写带来的影响,反映框架出本身的性能。从测试结果可以看出各种写法除了模板方法有点慢外,基本与纯JDBC相差不大。

Compare method execute time for repeat 10000 times:
pureJdbc: 0.983 s
dbUtilsWithConnMethod: 0.890 s
dbUtilsNoConnMethod: 0.968 s
nXxxJdbcStyle: 0.953 s
iXxxInlineStyle: 1.248 s
tXxxTemplateStyle: 3.714 s
tXxxTemplateAndInlineStyle: 3.589 s
tXxxNamingParamTemplateStyle: 3.730 s
dataMapperStyle: 2.91 s
activeRecordStyle: 1.687 s
activeRecordDefaultContext: 1.702 s

这个测试不包含NoSQL式查询,因为NoSql在本项目中目前只有查询方法,没有写方法,所以未加入对比测试。但是NoSql并不意味着性能低,因为是纯内存查询,而且在jSqlBox中,已经实现当节点之间有重复固定的查询时,会在节点之间形成缓存,查询性能反而可能比普通的SQL查询高出很多(目前jSqlBox项目中普通SQL的查询缓存还未开发)。
SAP财务模块必看:BAPI_ACC_DOCUMENT_POST扩展字段全攻略(含FIBF配置) 本文详细解析了SAP财务模块中如何利用BAPI_ACC_DOCUMENT_POST的EXTENSION1参数扩展自定义字段。文章从需求分析入手,对比了传统增强与BTE(业务交易事件)两种技术路径,并重点提供了基于FIBF配置的完整实战指南,包括数据结构理解、函数模块开发及配置步骤,帮助ABAP开发者和财务顾问高效、可维护地实现如WBS元素、税务代码等字段的灵活传递。 阅读详情

相关推荐

DIY便携式PEMF磁脉冲治疗仪:百元成本自制生物物理疗法设备

脉冲电磁场(PEMF)疗法是一种基于生物物理原理的非侵入性技术,它通过特定频率的电磁脉冲作用于细胞层面,影响细胞膜电位和离子通道活动,从而可能调节细胞代谢与功能。其技术价值在于提供了一种物理干预手段,在健康科技和生物黑客领域被探索用于放松、恢复等场景。实现PEMF的核心是通过信号发生与功率放大驱动电磁线圈产生可控磁场。本文以蓝牙音频模块和智能手机App为切入点,详细解析了如何利用低成本电子元件(如DFRobot XY-WRBT模块和漆包线线圈)构建一个可自定义频率的便携式PEMF设备,涵盖了从电路设计、线圈

weixin_31476015的博客 352

sqlbox的有关问题

1.     先装ensemble-2010.2.8.1104,再装sqldbx 2.SqlSetEnvAttr ODBC应用程序可以使用 SQLSetEnvAttr来启用连接池。 当ODBC应用程序调用 SQLDisconnect初次,该连接保存到池。任何后续 SQLConnect / SQLDisconnect匹配必需条件将重用第一个连接。 3 3、SQLDb

AStar的博客 1339

UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position 0-4: ordinal not in range(128)

python3报错处理:UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-4: ordinal not in range(128) 一、报错场景 // 报错代码 Traceback (most recent call last): File "test_autoo3_2.py", line 214, in <module> main() File "test_autoo3_2.py",

浪里小飞侠的博客 4201

探索高效SQL处理利器:jSqlBox

探索高效SQL处理利器:jSqlBox 是一个轻量级、高性能的Java SQL框架,它以简洁的API设计和强大的功能,为开发者提供了便捷的数据库操作体验。这个项目的出现,旨在简化数据库访问层的复杂性,提高开发效率,同时保持代码的整洁与可维护性。 技术概览 jSqlBox基于开源ORM(对象关系映射)框架MyBatis,但提供了更友好的面向方法的API。它支持动态SQL,并且集成了对事务管理的支持。...

gitblog_00094的博客 411

工业相机TEC散热技术:从原理到高精度成像的实战解析

本文深入解析了工业相机TEC散热技术的核心原理与实战应用。通过阐述热电制冷器如何利用帕尔帖效应实现精准温控,有效抑制图像传感器暗电流噪声,从而保障高精度成像的稳定性。文章结合工业场景痛点,提供了从TEC选型、系统集成到控制逻辑的完整设计指南,并分享了不同应用场景下的实战案例,是提升工业视觉系统成像质量的关键技术解析。

weixin_29269441的博客 414

基于Arduino与脑电模块的意念控制LED灯环实现

脑机接口(BCI)技术通过采集和处理脑电信号,实现人脑与外部设备的直接交互。其核心原理是利用传感器捕捉大脑活动产生的微弱电信号,经过放大、滤波和特征提取,转换为可识别的指令。这项技术在医疗康复、神经反馈训练和人机交互等领域具有重要价值。本文聚焦于一个具体的工程实践:使用消费级NeuroSky TGAM模块采集专注度与放松度数据,通过Arduino平台解析信号,并驱动WS2812B LED灯环实现实时可视化反馈。项目涵盖了硬件选型、电路搭建、数据解析和光效映射等关键环节,为脑机接口入门和嵌入式开发提供了完整的

weixin_28721917的博客 284

Mysql的持久层,jSqlBox

基于DbUtils内核的全功能数据库持久层工具 简介 | IntrojSqlBox是一个基于DbUtils内核开发的全功能开源Java数据库持久层工具,在架构、功能、易用性等方面都不输于其它持久层工具。与其它持久层工具对比请见与其它DAO工具对比可以对jSqlBox的功能与特点有一个大概的了解。架构 | Architecture优点 | Advantages架构合理:模块式架构,各个模块都可以脱...

weixin_39536010的博客 233

从充电管理芯片里‘偷师’:拆解一个PMOS双向开关电路,搞定你的固态继电器需求

本文深入解析了PMOS双向开关电路在固态继电器应用中的设计要点,重点探讨了防电流倒灌和高侧双向开关的实现技巧。通过拆解充电管理芯片中的PMOS背靠背结构,工程师可以构建高效可靠的固态开关方案,适用于智能硬件和电池管理系统。文章还提供了关键参数选择、电路优化建议及实测数据对比,助力解决电源防反接等工程难题。

weixin_33698043的博客 485

java jsql_历时一年,Java 持久层工具 jSqlBox 迎来首个正式版

历时一年开辟,Java持久层对象jSqlBox终于宣布1.0.0正式版,与以前的Snapshot版本相比,1.0.0版本是一个真正面向临盆情况的持久层对象了,可以用于实际开辟了。以下为jSqlBox项目最新版本内容介绍:jSqlBox是一个Java持久层对象,设计目标是用来代替今朝的Hibernate/MyBatis/JdbcTemplate/DbUtils等持久层对象。jSqlBox的重要特点:...

weixin_33340674的博客 318

基于SpringBoot+Vue的公司考勤管理系统

为什么选择我博主是优快云毕设辅导博客第一人兼开派祖师爷、博主本身从事开发软件开发、有丰富的编程能力和水平。专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流和合作。

m0_71849512的博客 1162

上位机图像处理和嵌入式模块部署(qmacvisual入门)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 虽然我们前面学习了很多的知识点,比如说在windows这边,用qt写界面,用opencv写图像处理代码;在linux这边先用树莓派测试,最后再部署到自己的平台上面。但是,对于一个商业软件是怎么运行的,很多时候我们心理还是没有底,或者说,自己写的软件距离一个商业软件还有多远,大部分学习的同学是不知道的。 就算有的同学说,这个时候可以借鉴一下vision pro,或者是

嵌入式-老费,一个分享专业嵌入式知识的blog 1231

总结一下Java中SQL的九种写法

因为在做持久层工具开发,现总结一下各种SQL的写法,也算是清晰一下自已的思路: 第一种写法 public void someMethod(){ try Connection conn=...; conn.setAutoCommit(false); PreparedStatement pst=...; ResultSet rs= executeSomeSq...

Drinkjava2的博客 3096

【深度学习基础】多任务学习(Multi-Task Learning)

多任务学习(Multi-task learning)是和单任务学习(single-task learning)相对的一种机器学习方法。在机器学习领域,标准的算法理论是一次学习一个任务,也就是系统的输出为实数的情况。复杂的学习问题先被分解成理论上独立的子问题,然后分别对每个子问题进行学习,最后通过对子问题学习结果的组合建立复杂问题的数学模型。多任务学习是一种联合学习,多个任务并行学习,结果相互影响。一次只学习一个任务(task),大部分的机器学习任务都属于单任务学习。

十二月的猫 1万+

历时一年开发,Java持久层工具jSqlBox终于推出第一个正式版

历时一年开发,Java持久层工具jSqlBox终于推出第一个正式版本1.0.0版。详见项目地址:https://gitee.com/drinkjava2/jSqlBox jSqlBox是一个Java持久层工具,设计目标是用来代替目前的Hibernate/MyBatis/JdbcTempl...

chuanxia1039的博客 359

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

java jsql,历时一年,Java 持久层工具 jSqlBox 迎来首个正式版

NoSql萌芽示例3 一 Java原生办法断定。用Java原生办法进行节点的断定,可以应用熟悉的Java说话并且支撑实体字段的重构。以下示例萌芽Email "e1"和"e5"所接洽关系的权限:@TestpublicvoidtestAutoPath2(){insertDemoData();TinyNetnet=ctx.netLoad(newEmail(),newUser(),...

weixin_42374596的博客 175

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

java各种优秀开源库收集

一、科学计算或矩阵运算库 科学计算包: JMathLib是一个用于计算复杂数学表达式并能够图形化显示计算结果的Java开源类库。它是Matlab、Octave、FreeMat、Scilab的一个克隆,但完全采用纯Java实现。 JSci:Java 科学对象(JSci)开放源代码项目是 Durham(英国 Durham)大学粒子理论中心的三年级研究生 Mark Hale 创立的。JSci 是一个包集合,包含数学和科学类。 使用 JSci,您既可以在 AWT 中也可以在 Swing 中创建简单的条形图、折线图

java之路 4644

Mysql的持久层_jSqlBox

基于DbUtils内核的全功能数据库持久层工具 简介 | IntrojSqlBox是一个全功能开源Java数据库持久层工具,在架构、功能、易用性等方面都不输于其它持久层工具,可以说,只要是与数据库操作相关的功能,jSqlBox都已具备,如DDL操作、分页、分库分表、声明式事务、分布式事务、关联映射查询等,所有这些功能都包含在一个1M大小的jar包中,不依赖任何第三方库。jSqlBox的最大特点是...

weixin_29470893的博客 333

java 数据库方言_jSqlBox 4.0.3 发布 支持多种数据库方言的DDL生成

jSqlBox是一个Java全功能数据库持久层工具,主页https://gitee.com/drinkjava2/jsqlboxjSqlBox主要特点是架构优、尺寸小、功能全,基本上所有与数据库操作相关的功能,jSqlBox都已提供。其它数据库工具的主要功能,jSqlBox通常都有对应的方案提供,不提供的往往是不重要甚至是反模式的功能,不信可以随便找一个其它持久层工具的功能,而jSqlBox却没有...

weixin_35042833的博客 531

jSqlBox 2.0.6 发布,轻松解决分库分表事务

开发四年只会写业务代码,分布式高并发都不会还做程序员? >>> jSqlBox是一个Java持久层工...

测试 629

发明了一种新的树结构数据库存储方案

最近在开发jSqlBox过程中,想研究一下树形结构和VO对象树的转换,突然发现一种新的树结构数据库存储方案,在网上搜索了一下,没有找到雷同的(也可能是我花的时间不够)方案,现介绍如下: 目前常见的树形结构数据库存储方案有以下四种,但是都存在一定问题: 1)Adjacency List::记录父节点。优点是简单,缺点是访问子树需要遍历,发出许多条SQL,对数据库压力大。 2)Path Enu...

Drinkjava2的博客 1011

HiveContext和SQLContext

使用SparkSQL时注意HiveContext和SQLContext的关系: 见源码: class HiveContext(sc : org.apache.spark.SparkContext) extends org.apache.spark.sql.SQLContext with org.apache.spark.Logging { SqlContext: 应该是对应spark

leehbing的博客 7221

3、SqlContext读取mysql数据,产生一个Dataframe

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34232617的博客 2046

CYQ.Data 支持 PostgreSQL 数据库

前言: 很久之前,就有同学问我CYQ.Data能不能支持下PostgreSQL,之后小做了下调查,发现这个数据库用的人少,加上各种因素,就一直没动手。 前两天,不小心看了一下Github上的消息: 看到这个问题又被重新提了出来了,于是,闹吧! 下面分享一下支持该数据库要处理的过程,让大伙明白CYQ.Data要支持一种新的数据库,需要花多少功夫。 1、找到数据库的驱动程序:Npgsql...

weixin_34059951的博客 210

Java多线程批量执行sql

当遇到大sql批量导入时几十万上百万数据,使用plsql执行等都是非常的慢。因此开发一套自定义线程池处理sql: 1,线程代码 import java.util.ArrayList; /** * @ClassName: com.ai.order.esb.yulang.tools.handle * @Description: TODO * @version: v1.0.0 * @a...

远方 8406

Java 实现异步调用

首先 我遇到的问题是 接口调用时需要更新缓存 而更新缓存又是个说快不快的过程 所以打算做异步调用 返回我所需要的结果即可 ,至于缓存什么时候更新完 就不是我所需要关注的了废话不多说 上代码public class MyExecutor {    private ExecutorService executor = Executors.newCachedThreadPool() ;    publi...

java_ying的博客 5万+
上一篇: 总结一下Java中SQL的九种写法
下一篇: 基于前序遍历的无递归的树形结构的数据库表设计
Drinkjava2
博客等级 码龄11年 9粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值