Java ORM framework on the open source market is not easy to use, so I spent a few days on my OrmKids, welcome to download and learn. Please pay attention to the public number into the group to discuss together.
OrmKids
MySQL single-table ORM framework that supports sub-database and sub-table is temporarily used for learning and will be tested in the production environment later
features
- Code concise, no dependencies, in addition to the use of the need to use MySQL driver
- Easy to use, no complex configuration required
- Provides automatic table creation
- Support sub – library and sub – table, can only sub – library, also can only sub – table
- Support groupby/having
- Support for native SQL
- Event callbacks are supported for service trace debugging and dynamic SQL rewriting
Multiple table association is not supported
- Multiple tables are more complex, high implementation cost, high learning cost, easy to make mistakes
- Common multi-table operations can be combined with multiple single-table operations
- In the case of separate database and table, multi-table operation is rarely used
- Do not use foreign keys and focus on SQL logic
db.withinTx
This approach can be used for complex multi-table queries and bulk data processing. Users can obtain native JDBC links by writing JDBC code.
Q
Users can use Q objects to build complex SQL queries
Other database support
Temporarily no
The entity interface
/** * All entity classes must implement this interface */
public interface IEntity {
/** * table name *@return* /
String table(a);
/** * subtables must override this method *@return* /
default String suffix(a) {
return null;
}
default String tableWithSuffix(a) {
return tableWith(suffix());
}
default String tableWith(String suffix) {
return Utils.tableWithSuffix(table(), suffix);
}
/** * defines table physical structure attributes such as engine=innodb for dynamically creating tables *@return* /
TableOptions options(a);
/** * defines the primary key and index information for the table to be dynamically created *@return* /
TableIndices indices(a);
}
Copy the code
Single form primary key
@Table
public class User implements IEntity {
@Column(name = "id", type = "int", autoincrement = true, nullable = false)
private Integer id;
@Column(name = "name", type = "varchar(255)", nullable = false)
private String name;
@Column(name = "nick", type = "varchar(255)", nullable = false)
private String nick;
@Column(name = "passwd", type = "varchar(255)")
private String passwd;
@Column(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")
private Date createdAt;
public User(a) {}public User(String name, String nick, String passwd) {
this.name = name;
this.nick = nick;
this.passwd = passwd;
}
public Integer getId(a) {
return id;
}
public String getName(a) {
return name;
}
public String getNick(a) {
return nick;
}
public String getPasswd(a) {
return passwd;
}
public Date getCreatedAt(a) {
return createdAt;
}
@Override
public TableOptions options(a) {
return new TableOptions().option("engine"."innodb");
}
@Override
public TableIndices indices(a) {
return new TableIndices().primary("id").unique("name");
}
@Override
public String table(a) {
return "user"; }}Copy the code
Single table compound primary key
@Table
public class Member implements IEntity {
@Column(name = "user_id", type = "int", nullable = false)
private Integer userId;
@Column(name = "group_id", type = "int", nullable = false)
private Integer groupId;
@Column(name = "title", type = "varchar(255)")
private String title;
@Column(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")
private Date createdAt;
public Member(a) {}public Member(Integer userId, Integer groupId, String title, Date createdAt) {
this.userId = userId;
this.groupId = groupId;
this.title = title;
this.createdAt = createdAt;
}
public Integer getUserId(a) {
return userId;
}
public Integer getGroupId(a) {
return groupId;
}
public String getTitle(a) {
return title;
}
public Date getCreatedAt(a) {
return createdAt;
}
@Override
public TableOptions options(a) {
return new TableOptions().option("engine"."innodb");
}
@Override
public TableIndices indices(a) {
return new TableIndices().primary("user_id"."group_id");
}
@Override
public String table(a) {
return "member"; }}Copy the code
Depots interface
public interface IGridable<T extends IEntity> {
/** * Select index */ based on entity object
int select(int dbs, T t);
/** * Select the sub-index */ based on specific parameters
int select(int dbs, Object... params);
}
Copy the code
Depots table
@Table
public class BookShelf implements IEntity {
public final static int PARTITIONS = 4;
@Column(name = "user_id", type = "varchar(255)", nullable = false)
private String userId;
@Column(name = "book_id", type = "varchar(255)", nullable = false)
private String bookId;
@Column(name = "comment", type = "varchar(255)")
private String comment;
@Column(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")
private Date createdAt;
public BookShelf(a) {}public BookShelf(String userId, String bookId, String comment, Date createdAt) {
this.userId = userId;
this.bookId = bookId;
this.comment = comment;
this.createdAt = createdAt;
}
public String getUserId(a) {
return userId;
}
public String getBookId(a) {
return bookId;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getComment(a) {
return comment;
}
public Date getCreatedAt(a) {
return createdAt;
}
@Override
public String table(a) {
return "book_shelf";
}
@Override
public TableOptions options(a) {
return new TableOptions().option("engine"."innodb");
}
@Override
public TableIndices indices(a) {
return new TableIndices().primary("user_id"."book_id");
}
/* * Split table policy */
@Override
public String suffix(a) {
var crc32 = new CRC32();
crc32.update(userId.getBytes(Utils.UTF8));
return String.valueOf(Math.abs(crc32.getValue()) % PARTITIONS);
}
/** ** branch library policy */
public static class GridStrategy<D extends DB> implements IGridable<BookShelf> {
@Override
public int select(int dbs, BookShelf t) {
return Math.abs(t.getUserId().hashCode()) % dbs;
}
@Override
public int select(int dbs, Object... params) {
String userId = (String) params[0];
returnMath.abs(userId.hashCode()) % dbs; }}}Copy the code
Defining a single database
public class DemoDB extends DB {
private DataSource ds;
public DemoDB(String name, String uri) {
this(name, new HashMap<>(), uri);
}
public DemoDB(String name, Map<Class<? extends IEntity>, Meta> metas, String uri) {
super(name, metas);
var ds = new MysqlConnectionPoolDataSource(); / / the connection pool
ds.setUrl(uri);
this.ds = ds;
}
@Override
protected Connection conn(a) { // Get the link
try {
return ds.getConnection();
} catch (SQLException e) {
throw newKidsException(e); }}}Copy the code
Define grid database – sub – library
public class GridDemoDB extends GridDB<DemoDB> {
/** * pass in multiple DB objects */
public GridDemoDB(DemoDB[] dbs) {
super(dbs);
this.registerGridables();
}
/* * Register the entity class branch policy */
@Override
public void registerGridables(a) {
this.gridWith(BookShelf.class, newBookShelf.GridStrategy<DemoDB>()); }}Copy the code
Single form primary key add delete change check
public class DemoSimplePk {
private final static String URI = "jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";
public static void main(String[] args) {
var db = new DemoDB("demo", URI);
try {
db.create(User.class); / / create a table
var user = new User("test1"."nick1"."passwd1");
db.insert(user); / / insert
System.out.println(user.getId());
user = db.get(User.class, user.getId()); // Primary key query
System.out.printf("%s %s %s %s %s\n", user.getId(), user.getName(), user.getNick(), user.getPasswd(),
user.getCreatedAt());
user = new User("test2"."nick2"."passwd2");
db.insert(user); / / insert again
var count = db.count(User.class); // Query the total number of rows
System.out.println(count);
var users = db.find(User.class); // Lists all rows
System.out.println(users.size());
for (var u : users) {
System.out.printf("%s %s %s %s %s\n", u.getId(), u.getName(), u.getNick(), u.getPasswd(),
u.getCreatedAt());
}
users = db.find(User.class, Q.eq_("nick"), "nick2"); // Conditional query
System.out.println(users.size());
var setters = new HashMap<String, Object>();
setters.put("passwd"."whatever");
db.update(User.class, setters, 2); / / modify
users = db.find(User.class); // List all rows again
System.out.println(users.size());
for (var u : users) {
System.out.printf("%s %s %s %s %s\n", u.getId(), u.getName(), u.getNick(), u.getPasswd(),
u.getCreatedAt());
}
db.delete(User.class, 1); / / delete
db.delete(User.class, 2); / / delete again
count = db.count(User.class); // Count all rows
System.out.println(count);
} finally {
db.drop(User.class); / / delete table}}}Copy the code
Single table compound primary key add delete change check
public class DemoCompoundPk {
private final static String URI = "jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";
public static void main(String[] args) {
var db = new DemoDB("demo", URI);
try {
db.create(Member.class); / / table
var member = new Member(1.2."boss".null);
db.insert(member); / / insert
member = db.get(Member.class, 1.2); // Primary key query
System.out.println(member.getTitle());
member = new Member(2.2."manager".new Date());
db.insert(member); / / insert again
var count = db.count(Member.class); // Get the total number of rows
System.out.println(count);
var members = db.find(Member.class); // Get all rows
for (var m : members) {
System.out.printf("%d %d %s %s\n", m.getUserId(), m.getGroupId(), m.getTitle(), m.getCreatedAt());
}
member = new Member(2.3."manager".new Date());
db.insert(member); / / insert again
members = db.find(Member.class, Q.eq_("group_id"), 2); // Conditional query
for (var m : members) {
System.out.printf("%d %d %s %s\n", m.getUserId(), m.getGroupId(), m.getTitle(), m.getCreatedAt());
}
var setters = new HashMap<String, Object>();
setters.put("title"."employee");
db.update(Member.class, setters, 2.3); / / modify
member = db.get(Member.class, 2.3); // Primary key query
System.out.println(member.getTitle());
db.delete(Member.class, 1.2); / / delete
db.delete(Member.class, 2.2); / / delete
db.delete(Member.class, 2.3); / / delete
count = db.count(Member.class); // Get the total number of rows
System.out.println(count);
} finally {
db.drop(Member.class); / / delete table}}}Copy the code
Complex queries
public class DemoComplexQuery {
private final static String URI = "jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";
public static void main(String[] args) {
var db = new DemoDB("demo", URI);
try {
db.create(Exam.class); / / table
var random = new Random();
for (var i = 0; i < 100; i++) {
var userId = Math.abs(random.nextLong());
var exam = new Exam(userId, random.nextInt(100), random.nextInt(100), random.nextInt(100),
random.nextInt(100), random.nextInt(100), random.nextInt(100));
db.insert(exam); / / insert
}
System.out.println(db.count(Exam.class)); // Query the total number of rows
// math >= 50
var exams = db.find(Exam.class, Q.ge_("math"), 50); // Conditional query
System.out.println(exams.size());
var count = db.count(Exam.class, Q.ge_("math"), 50); // Total number of conditional rows
System.out.println(count);
// math > 50 & english >= 50
exams = db.find(Exam.class, Q.and(Q.gt_("math"), Q.ge_("english")), 50.50); // Conditional query
System.out.println(exams.size());
count = db.count(Exam.class, Q.and(Q.gt_("math"), Q.ge_("english")), 50.50); // Total number of conditional rows
System.out.println(count);
// math > 50 || english >= 50
exams = db.find(Exam.class, Q.or(Q.gt_("math"), Q.ge_("english")), 50.50); // Conditional query
System.out.println(exams.size());
count = db.count(Exam.class, Q.or(Q.gt_("math"), Q.ge_("english")), 50.50); // Total number of conditional rows
System.out.println(count);
// math > 50 && (english >= 50 || chinese > 60)
exams = db.find(Exam.class, Q.and(Q.gt_("math"), Q.or(Q.ge_("english"), Q.gt_("chinese"))), 50.50.60); // Conditional query
System.out.println(exams.size());
count = db.count(Exam.class, Q.and(Q.gt_("math"), Q.or(Q.ge_("english"), Q.gt_("chinese"))), 50.50.60); // Total number of conditional rows
System.out.println(count);
// math > 50 || physics between 60 and 80 || chemistry < 60
exams = db.find(Exam.class, Q.or(Q.gt_("math"), Q.between_("physics"), Q.lt_("chemistry")), 50.60.80.60); // Conditional query
System.out.println(exams.size());
count = db.count(Exam.class, Q.or(Q.gt_("math"), Q.between_("physics"), Q.lt_("chemistry")), 50.60.80.60); // Total number of conditional rows
System.out.println(count);
// group by math / 10
var q = Q.select().field("(math div 10) * 10 as mathx"."count(1)").table("exam").groupBy("mathx")
.having(Q.gt_("count(1)")).orderBy("count(1)"."desc"); // Complex SQL constructs
var rank = new LinkedHashMap<Integer, Integer>();
db.any(Exam.class, q, stmt -> { // Native SQL query
stmt.setInt(1.0);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
rank.put(rs.getInt(1), rs.getInt(2));
}
return rs;
});
rank.forEach((mathx, c) -> {
System.out.printf("[%d-%d) = %d\n", mathx, mathx + 10, c);
});
} finally{ db.drop(Exam.class); }}}Copy the code
table
public class DemoPartitioning {
private final static String URI = "jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";
public static void main(String[] args) {
var db = new DemoDB("demo", URI);
try {
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
db.create(BookShelf.class, String.valueOf(i)); // Create all subtables
}
var bss = new ArrayList<BookShelf>();
for (int i = 0; i < 100; i++) {
var bs = new BookShelf("user" + i, "book" + i, "comment" + i, new Date());
bss.add(bs);
db.insert(bs); // Insert the corresponding table automatically
}
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
System.out.printf("partition %d count %d\n", i, db.count(BookShelf.class, String.valueOf(i)));
}
Random random = new Random();
for (var bs : bss) {
bs.setComment("comment_update_" + random.nextInt(100));
db.update(bs); // Update, automatically update the corresponding table data
}
bss = new ArrayList<BookShelf>();
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
bss.addAll(db.find(BookShelf.class, String.valueOf(i))); // Specify a table to list all rows
}
for (var bs : bss) {
System.out.println(bs.getComment());
}
for (var bs : bss) {
db.delete(bs); // Delete table data one by one}}finally {
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
db.drop(BookShelf.class, String.valueOf(i)); // Delete all subtables}}}}Copy the code
depots
public class DemoSharding {
private static DemoDB[] dbs = new DemoDB[3];
static {
Map<Class<? extends IEntity>, Meta> metas = new HashMap<>();
dbs[0] = new DemoDB("demo-0", metas,
"jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");
dbs[1] = new DemoDB("demo-1", metas,
"jdbc:mysql://localhost:3307/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");
dbs[2] = new DemoDB("demo-2", metas,
"jdbc:mysql://localhost:3308/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");
}
public static void main(String[] args) {
var grid = new GridDemoDB(dbs); // Construct Grid instance
try {
for (int k = 0; k < BookShelf.PARTITIONS; k++) {
grid.create(BookShelf.class, String.valueOf(k)); // Create tables in all branches
}
var bss = new ArrayList<BookShelf>();
for (int i = 0; i < 100; i++) {
var bs = new BookShelf("user" + i, "book" + i, "comment" + i, new Date());
bss.add(bs);
grid.insert(bs); // insert, automatically distributed to the corresponding sub-table in the sub-library
}
for (int k = 0; k < grid.size(); k++) {
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
System.out.printf("db %d partition %d count %d\n", k, i,
grid.count(BookShelf.class, k, String.valueOf(i))); // select * from table where row number = 1
}
}
Random random = new Random();
for (var bs : bss) {
bs.setComment("comment_update_" + random.nextInt(100));
grid.update(bs); // Update, automatically distributed to the corresponding sub-table in the sub-library
}
for (var bs : bss) {
bs = grid.get(BookShelf.class, bs.getUserId(), bs.getBookId()); // the primary key query is automatically distributed to the corresponding sub-table in the sub-database
System.out.println(bs.getComment());
}
for (var bs : bss) {
grid.delete(bs); // delete, automatically distribute to the corresponding sub-table in the sub-library
}
for (int k = 0; k < grid.size(); k++) {
for (int i = 0; i < BookShelf.PARTITIONS; i++) {
System.out.printf("db %d partition %d count %d\n", k, i,
grid.count(BookShelf.class, k, String.valueOf(i))); // select * from table where row number = 1}}}finally {
for (int k = 0; k < BookShelf.PARTITIONS; k++) {
grid.drop(BookShelf.class, String.valueOf(k)); // Delete all subtables in the database}}}}Copy the code
Event context object
public class Context {
private DB db; // Database instance
private Connection conn; // The current link
private Class<? extends IEntity> clazz; // The current entity class
private Q q; / / query SQL
private Object[] values; // Query binding parameters
private boolean before; // before or after
private Exception error; / / exception
private long duration; / / take microsecond
}
Copy the code
Event callback
public class DemoEvent {
private final static String URI = "jdbc:mysql://localhost:3306/mydrc? user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";
public static void main(String[] args) {
var db = new DemoDB("demo", URI);
db.on(ctx -> { // Global event callback
System.out.printf("db=%s sql=%s cost=%dus\n", ctx.db().name(), ctx.q().sql(), ctx.duration());
return true; // Returning false causes the chain of events to terminate and subsequent ORM operations will not be performed
});
try {
db.create(User.class);
db.scope(ctx -> { // Scope callback. All ORM operations inside the execute method are called back
System.out.printf("db=%s sql=%s cost=%dus\n", ctx.db().name(), ctx.q().sql(), ctx.duration());
return true;
}).execute(() -> {
db.count(User.class);
db.find(User.class);
});
} finally {
db.drop(User.class); / / delete table}}}Copy the code