<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Forb Yuan</title>
    <description>The latest articles on DEV Community by Forb Yuan (@f0rb).</description>
    <link>https://dev.to/f0rb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1250037%2F225f45ac-4d45-40af-a348-a55ba7c5391f.jpeg</url>
      <title>DEV Community: Forb Yuan</title>
      <link>https://dev.to/f0rb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/f0rb"/>
    <language>en</language>
    <item>
      <title>What‘s the Real Problem for ORMs</title>
      <dc:creator>Forb Yuan</dc:creator>
      <pubDate>Thu, 19 Dec 2024 14:21:35 +0000</pubDate>
      <link>https://dev.to/f0rb/whats-the-real-problem-for-orms-5hal</link>
      <guid>https://dev.to/f0rb/whats-the-real-problem-for-orms-5hal</guid>
      <description>&lt;p&gt;The designers and developers of ORMs have never truly understood the real problems they need to address. Once this problem is solved, the ORM will no longer be an ORM.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dynamic Query Problem and Combinatorial Mathematics
&lt;/h2&gt;

&lt;p&gt;Let's revisit the dynamic query problem first.&lt;/p&gt;

&lt;p&gt;In an information system, developers provide users with a query interface containing &lt;code&gt;n&lt;/code&gt; query parameters. The user fills out &lt;code&gt;k&lt;/code&gt; of these parameters to make a request $(k \in [0, n])$. The system constructs a query statement based on these &lt;code&gt;k&lt;/code&gt; parameters. Developers need to combine the corresponding query conditions for every possible combination of &lt;code&gt;k&lt;/code&gt; query parameters, which is known as the dynamic query problem.&lt;/p&gt;

&lt;p&gt;The core of this problem is the introduction of an uncertain variable &lt;strong&gt;k&lt;/strong&gt; by the end-user. SQL, in its original design, was only intended for developers and did not account for user input. Therefore, each SQL statement is static, corresponding to only one set of query conditions. Since the ORM framework does not address this issue, developers need to combine the corresponding query conditions based on the user's input.&lt;/p&gt;

&lt;p&gt;Assuming the &lt;code&gt;n&lt;/code&gt; query parameters are independent and the order of the query conditions is fixed, this problem can be described using combinatorial mathematics as a subset selection problem: choosing &lt;code&gt;k&lt;/code&gt; elements from a set of &lt;code&gt;n&lt;/code&gt; elements provided by the query interface to form a subset.&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
\sum_{k=0}^{n} \binom{n}{k} = 2^n&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;From the above formula, we can see that for a query interface with &lt;code&gt;n&lt;/code&gt; query parameters, there are $2^n$ possible query clauses. For example, if there are 3 query parameters, 8 query clauses need to be written; if there are 10 query parameters, 1024 query clauses are needed. Both writing and maintaining this are extremely difficult.&lt;/p&gt;

&lt;p&gt;ORM frameworks have not paid attention to this problem, nor have they provided a solution. A natural way is to use &lt;code&gt;if&lt;/code&gt; statements to check the query parameters and determine whether the corresponding query condition should be concatenated. Since each &lt;code&gt;if&lt;/code&gt; statement produces two possible outcomes (TRUE or FALSE), corresponding to whether or not to execute the query condition concatenation within the &lt;code&gt;if&lt;/code&gt; block, the &lt;code&gt;n&lt;/code&gt; &lt;code&gt;if&lt;/code&gt; statements result in $2^n$ possible concatenation outcomes.&lt;/p&gt;

&lt;p&gt;At first glance, this problem seems to be solved, but the issue with this approach is that developers must write an &lt;code&gt;if&lt;/code&gt; statement for each query parameter. As the number of query parameters increases, the number of &lt;code&gt;if&lt;/code&gt; statements grows, and code maintenance still remains difficult.&lt;/p&gt;

&lt;p&gt;This is the real problem that needs to be solved in database access, instead of just mapping object models to relational models.&lt;/p&gt;
&lt;h2&gt;
  
  
  Query Object Mapping Method
&lt;/h2&gt;

&lt;p&gt;When we use the &lt;em&gt;Introduce Parameter Object&lt;/em&gt; refactoring method to group all the query parameters into a single object, we get an object with &lt;code&gt;n&lt;/code&gt; fields, and the construction of query clauses depends solely on this object.&lt;/p&gt;

&lt;p&gt;For an object with &lt;code&gt;n&lt;/code&gt; fields, each field can either be assigned or unassigned, resulting in $2^n$ possible combinations of assignments. If each field maps to a query condition, we can use the $2^n$ combinations of assignments to construct $2^n$ query clauses.&lt;/p&gt;

&lt;p&gt;Thus, we shift our approach to map query clauses through an object instead of mapping objects to relations. We call this object used to construct query clauses a &lt;strong&gt;Query Object&lt;/strong&gt;, and the method of combining query conditions into query clauses based on the field assignments of the Query Object is called the &lt;strong&gt;Query Object Mapping Method&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Object Mapping
&lt;/h3&gt;

&lt;p&gt;For programming languages that support reflection, we can use reflection to read the values of the fields and determine whether each field's assignment should be mapped to a query condition. The algorithm is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traverse the fields of the Query Object instance.&lt;/li&gt;
&lt;li&gt;Use reflection to obtain the value of each field and map the assigned fields to query conditions.&lt;/li&gt;
&lt;li&gt;Use logical operators such as AND to combine the query conditions into query clauses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One simple way to map fields to query conditions is by using annotations to declare query conditions along with the fields.&lt;/p&gt;

&lt;p&gt;Reflection and annotation declarations help us encapsulate the query clause construction code into a framework that is available for all developers to use, greatly simplifying their work.&lt;/p&gt;

&lt;p&gt;For example, for a &lt;code&gt;UserQuery&lt;/code&gt; object defined by developers, the framework can use the algorithm described above to construct the corresponding query clause based on the &lt;code&gt;UserQuery&lt;/code&gt; assignment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserQuery&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Condition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name LIKE CONCAT('%', ?, '%')"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;nameLike&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Condition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"age &amp;gt; ?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;ageGt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Condition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"valid = ?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// a possible query clause: WHERE age &amp;gt; ? and valid = ?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After confirming the advantages of object mapping over the &lt;code&gt;if&lt;/code&gt; statement concatenation approach, we further discuss how to construct different query conditions based on field characteristics to avoid maintaining string conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Field Mapping
&lt;/h3&gt;

&lt;p&gt;In SQL, query conditions mainly include the following three types:&lt;/p&gt;

&lt;p&gt;Here is the translation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comparison Query Conditions&lt;/strong&gt;: For example, &lt;code&gt;age &amp;gt; ?&lt;/code&gt;, this condition is represented using &lt;strong&gt;Predicate Logic&lt;/strong&gt;. Predicate logic is used to express basic comparison operations, such as equality (=), greater than (&amp;gt;), less than (&amp;lt;), greater than or equal to (≥), less than or equal to (≤), and not equal to (≠).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logical Query Conditions&lt;/strong&gt;: These are formed using &lt;strong&gt;Boolean Algebra&lt;/strong&gt;, combining multiple conditions with logical operators such as &lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;OR&lt;/code&gt;, and &lt;code&gt;NOT&lt;/code&gt;. These conditions express the logical relationships between multiple query conditions. For example, &lt;code&gt;age &amp;gt; 30 AND valid = true&lt;/code&gt; is a logical query condition where two conditions are combined using the "AND" operator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subquery Conditions&lt;/strong&gt;: These conditions involve a nested query, typically based on &lt;strong&gt;Relational Algebra&lt;/strong&gt;. Relational algebra provides the mathematical foundation for database queries, used to express relationships between tables and nested queries. For example, &lt;code&gt;age &amp;gt; (SELECT avg(age) FROM t_user)&lt;/code&gt; is a subquery condition, where &lt;code&gt;avg(age)&lt;/code&gt; is a subquery that returns a result and is compared with a field in the outer query.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on these mathematical principles, we design three types of fields to construct query conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapping Comparison Query Conditions via Predicate Suffix Fields&lt;/strong&gt;: Comparison query conditions are usually composed of column names, comparison operators, and parameters. In DSLs (domain-specific languages), predicate phrases are often used to represent comparison operators. For example, &lt;code&gt;eq&lt;/code&gt; represents equality (&lt;code&gt;=&lt;/code&gt;), &lt;code&gt;gt&lt;/code&gt; represents greater than (&lt;code&gt;&amp;gt;&lt;/code&gt;), and so on. &lt;code&gt;condition.gt("age", 30)&lt;/code&gt; represents the query condition &lt;code&gt;age &amp;gt; 30&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We append the predicate phrase to the column name to represent the query condition. For example, the field &lt;code&gt;ageGt&lt;/code&gt; represents the query condition &lt;code&gt;age &amp;gt; ?&lt;/code&gt;. Similar suffixes include &lt;code&gt;Eq&lt;/code&gt;, &lt;code&gt;Ne&lt;/code&gt;, &lt;code&gt;Ge&lt;/code&gt;, &lt;code&gt;Lt&lt;/code&gt;, &lt;code&gt;Le&lt;/code&gt;, &lt;code&gt;In&lt;/code&gt;, &lt;code&gt;NotIn&lt;/code&gt;, &lt;code&gt;Null&lt;/code&gt;, &lt;code&gt;Like&lt;/code&gt;, etc., which allows us to map various comparison query conditions through field suffixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constructing Logical Query Conditions via Logic Suffix Fields&lt;/strong&gt;: Logical query conditions are a group of query conditions connected by logical operators such as AND or OR.&lt;/p&gt;

&lt;p&gt;The type of logic suffix fields is either a collection or a query object used to construct multiple query conditions, where each element or field corresponds to one query condition.&lt;/p&gt;

&lt;p&gt;The name of the logical suffix field includes the logical suffix &lt;code&gt;And&lt;/code&gt; or &lt;code&gt;Or&lt;/code&gt;, indicating the logical operator that connects multiple query conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constructing Subquery Conditions via Subquery Fields&lt;/strong&gt;: Subquery fields should be query objects.&lt;/p&gt;

&lt;p&gt;For example, for a subquery condition like &lt;code&gt;age &amp;gt; (SELECT avg(age) FROM t_user)&lt;/code&gt;, we can break it down into three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the condition part &lt;code&gt;age &amp;gt;&lt;/code&gt;, we can reuse the mapping method for predicate suffix fields. However, to avoid naming conflicts with the existing predicate suffix field &lt;code&gt;ageGt&lt;/code&gt;, we need to add distinguishing characters, such as &lt;code&gt;ageGtAvg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For the main subquery part &lt;code&gt;SELECT avg(age) FROM t_user&lt;/code&gt;, we can declare the column name and table name using annotations like &lt;code&gt;@Subquery(select = "avg(age)", from = "t_user")&lt;/code&gt;, or define it in the field name like &lt;code&gt;ageGtAvgAgeOfUser&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For the WHERE clause of the subquery, we can reuse the query object mapping method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these three types of fields, we can automatically construct most query conditions. For other query conditions, we can continue developing new methods to support them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Achievement
&lt;/h3&gt;

&lt;p&gt;Through object and field mapping, the query object now has the following four key characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Constructs comparison query conditions;&lt;/li&gt;
&lt;li&gt;Constructs logical query conditions;&lt;/li&gt;
&lt;li&gt;Constructs subquery conditions;&lt;/li&gt;
&lt;li&gt;Dynamically combines query conditions based on query parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From a mathematical theory perspective, the query object mapping method effectively addresses the uncertainty introduced by end users and constructs various query conditions in SQL, forming an object-based dynamic query language. &lt;/p&gt;

&lt;p&gt;Compared to the static query clauses in SQL, this dynamic query language provides the additional feature of dynamically combining query conditions. On this basis, we can continue constructing other parts of the SQL statement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementations
&lt;/h2&gt;

&lt;p&gt;The query object mapping method constructs corresponding query conditions solely through the field metadata, making it applicable to any object-oriented programming language. Here are examples in Java and Go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserQuery&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// WHERE&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;nameLike&lt;/span&gt;       &lt;span class="c1"&gt;// AND name LIKE ?&lt;/span&gt;
  &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;ageGt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// AND age &amp;gt; ?&lt;/span&gt;
  &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;ageLe&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// AND age &amp;lt;= ?&lt;/span&gt;
  &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// AND valid = ?&lt;/span&gt;
  &lt;span class="nc"&gt;UserQuery&lt;/span&gt; &lt;span class="n"&gt;userOr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// AND (age &amp;gt; ? OR age &amp;lt;= ? OR valid = ?)&lt;/span&gt;
  &lt;span class="nd"&gt;@Subquery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"avg(age)"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"t_user"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;UserQuery&lt;/span&gt; &lt;span class="n"&gt;ageGtAvg&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// AND age &amp;gt; (SELECT avg(age) FROM t_user [WHERE])&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="http://github.com/doytowin/doyto-query" rel="noopener noreferrer"&gt;http://github.com/doytowin/doyto-query&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserQuery&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c"&gt;// WHERE&lt;/span&gt;
    &lt;span class="n"&gt;NameLike&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;       &lt;span class="c"&gt;// AND name LIKE ?&lt;/span&gt;
    &lt;span class="n"&gt;AgeGt&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;          &lt;span class="c"&gt;// AND age &amp;gt; ?&lt;/span&gt;
    &lt;span class="n"&gt;AgeLe&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;          &lt;span class="c"&gt;// AND age &amp;lt;= ?&lt;/span&gt;
    &lt;span class="n"&gt;Valid&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;         &lt;span class="c"&gt;// AND valid = ?&lt;/span&gt;
    &lt;span class="n"&gt;UserOr&lt;/span&gt;   &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;UserQuery&lt;/span&gt;  &lt;span class="c"&gt;// AND&lt;/span&gt;

 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="n"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="n"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                           &lt;span class="c"&gt;// AND age &amp;gt; (SELECT avg(age) FROM t_user [WHERE])&lt;/span&gt;
    &lt;span class="n"&gt;ScoreGtAvg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserQuery&lt;/span&gt; &lt;span class="s"&gt;`subquery:"select:avg(age),from:t_user"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="http://github.com/doytowin/goooqo" rel="noopener noreferrer"&gt;http://github.com/doytowin/goooqo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each field corresponds to one or more query conditions, and the conditions are combined based on field assignments to form the final query clause. Logical and subquery conditions can also be constructed by reusing query objects. These advantages are not available in SQL, which is a static language.&lt;/p&gt;

&lt;p&gt;By defining query objects in this way, &lt;strong&gt;developers no longer need to explicitly write &lt;code&gt;if&lt;/code&gt; statements to concatenate query conditions&lt;/strong&gt;. The framework can use reflection to read the assignment of each field and implicitly include the &lt;strong&gt;assignment checks and query condition concatenation in the framework code&lt;/strong&gt;, greatly simplifying the code writing and maintenance for dynamic queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;For pagination and sorting, we can still define the relevant parameters in the query object and construct the pagination and sorting clauses based on these parameters when generating the SQL statement. We simply need to declare that these parameters are not used to construct query conditions.&lt;/p&gt;

&lt;p&gt;We can also generate the previously manually written &lt;code&gt;if&lt;/code&gt; statement concatenation code for the query object through code generation. This would eliminate the performance impact of reflection and support programming languages that do not provide reflection.&lt;/p&gt;

&lt;p&gt;Furthermore, the query object can also be used to construct MongoDB queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"$gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"$lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"memo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"memo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"$regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"$eq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"$or"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}]}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The empty objects are placeholders similar to SQL.)&lt;/p&gt;

&lt;p&gt;Since all query languages are designed based on the same mathematical principles, databases like MongoDB, Redis, and ElasticSearch have compared and converted their query languages to SQL. Some databases even directly support portions of the SQL standard. Therefore, the query object mapping method we designed based on these mathematical principles is not only applicable to all object-oriented programming languages, but also to all database query languages. This goes beyond the theoretical scope of ORM.&lt;/p&gt;

&lt;p&gt;As for complex queries involving aggregation and joins, these can be constructed using a view object mapping method, which is beyond the scope of this paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This paper categorizes the dynamic query problem as a subset selection problem in combinatorial mathematics, proposes directly using query object assignments to handle the assignment combinations of query conditions, and fully utilizes the characteristics of objects and fields to construct various query conditions in SQL. This leads to the development of an object-based dynamic query language, becoming a more efficient database access solution than ORM.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What?! Just Two Lines to Handle CRUD Web APIs in Golang?</title>
      <dc:creator>Forb Yuan</dc:creator>
      <pubDate>Mon, 26 Aug 2024 14:42:32 +0000</pubDate>
      <link>https://dev.to/f0rb/what-just-two-lines-to-handle-crud-web-apis-in-golang-775</link>
      <guid>https://dev.to/f0rb/what-just-two-lines-to-handle-crud-web-apis-in-golang-775</guid>
      <description>&lt;p&gt;This is all thanks to the support of OQM (Object Query-Language Mapping) technology, we developed a Go version called &lt;a href="https://github.com/doytowin/goooqo" rel="noopener noreferrer"&gt;GoooQo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to OQM
&lt;/h2&gt;

&lt;p&gt;The biggest difference between OQM technology and traditional ORM (object-relational mapping) technology is that OQM proposes to build CRUD statements directly through objects.&lt;/p&gt;

&lt;p&gt;The core function of OQM is to build a query clause through a query object, which is the origin of the &lt;code&gt;Q&lt;/code&gt; in the name of OQM.&lt;/p&gt;

&lt;p&gt;Another significant discovery in OQM technology is that the field names in query objects and the conditions in query clauses can be converted interchangeably.&lt;/p&gt;

&lt;p&gt;For example, in SQL, the query condition &lt;code&gt;age &amp;gt; ?&lt;/code&gt; can be transformed by rewriting &lt;code&gt;&amp;gt;&lt;/code&gt; as the string alias &lt;code&gt;Gt&lt;/code&gt; (short for Greater Than), and then attaching &lt;code&gt;Gt&lt;/code&gt; as a suffix to the column name &lt;code&gt;age&lt;/code&gt;, resulting in &lt;code&gt;ageGt&lt;/code&gt;, which can be used as a field name.&lt;/p&gt;

&lt;p&gt;Then we can use OQM technology to parse such objects. When a field named &lt;code&gt;ageGt&lt;/code&gt; is assigned a value, the corresponding query condition can be generated by reversing the above process.&lt;/p&gt;

&lt;p&gt;In this way, we only need to create an entity object and a query object to build CRUD statements. The entity object is used to determine the table name and the column names, and the instance of the query object is used to control the construction of the query clause​​.&lt;/p&gt;

&lt;p&gt;By encapsulating all the boilerplate code, the code to build a CRUD Web API for a single table is reduced to just two lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Introduction
&lt;/h2&gt;

&lt;p&gt;We developed a separate &lt;a href="https://github.com/doytowin/goooqo-demo" rel="noopener noreferrer"&gt;demo&lt;/a&gt; to demonstrate the features of GoooQo.&lt;/p&gt;

&lt;p&gt;The file &lt;a href="https://github.com/doytowin/goooqo-demo/blob/main/test.db" rel="noopener noreferrer"&gt;test.db&lt;/a&gt; is a SQLite database containing a table &lt;code&gt;t_user&lt;/code&gt; with four rows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;mobile&lt;/th&gt;
&lt;th&gt;nickname&lt;/th&gt;
&lt;th&gt;memo&lt;/th&gt;
&lt;th&gt;valid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;f0rb&lt;/td&gt;
&lt;td&gt;123456&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:f0rb@163.com"&gt;f0rb@163.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18888888881&lt;/td&gt;
&lt;td&gt;test1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;user2&lt;/td&gt;
&lt;td&gt;123456&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:test2@qq.com"&gt;test2@qq.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18888888882&lt;/td&gt;
&lt;td&gt;test2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;user3&lt;/td&gt;
&lt;td&gt;123456&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:test3@qq.com"&gt;test3@qq.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18888888883&lt;/td&gt;
&lt;td&gt;test3&lt;/td&gt;
&lt;td&gt;memo&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;user4&lt;/td&gt;
&lt;td&gt;123456&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:test4@qq.com"&gt;test4@qq.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18888888884&lt;/td&gt;
&lt;td&gt;test4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using GoooQo, we only need to define two structs, &lt;code&gt;UserEntity&lt;/code&gt; and &lt;code&gt;UserQuery&lt;/code&gt;, for the user table in &lt;a href="https://github.com/doytowin/goooqo-demo/blob/main/user.go" rel="noopener noreferrer"&gt;user.go&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;UserEntity&lt;/code&gt; is a traditional entity object with fields corresponding to the columns in the user table.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UserQuery&lt;/code&gt; is dedicated to constructing query clauses. The fields in &lt;code&gt;UserQuery&lt;/code&gt; follow the format of column name plus suffix (refer to the suffix mapping table in the appendix) to generate the corresponding query conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserQuery&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;goooqo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageQuery&lt;/span&gt;
    &lt;span class="n"&gt;IdGt&lt;/span&gt;         &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;     &lt;span class="c"&gt;// id &amp;gt; ?&lt;/span&gt;
    &lt;span class="n"&gt;IdIn&lt;/span&gt;         &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;   &lt;span class="c"&gt;// id IN (?,?,?)&lt;/span&gt;
    &lt;span class="n"&gt;EmailContain&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;  &lt;span class="c"&gt;// email LIKE "%value%"&lt;/span&gt;
    &lt;span class="n"&gt;MemoNull&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// memo is [NOT] NULL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, the embedded &lt;code&gt;goooqo.PageQuery&lt;/code&gt; defines three fields: &lt;code&gt;PageNumber&lt;/code&gt;, &lt;code&gt;PageSize&lt;/code&gt;, and &lt;code&gt;Sort&lt;/code&gt;, which are used to construct the paging clause and the sorting clause.&lt;/p&gt;

&lt;p&gt;After establishing a database connection in the main method, the following two lines of code are all we need to build the CRUD APIs for the user table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;userDataAccess&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTxDataAccess&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserEntity&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;tm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;goooqo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuildRestService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserEntity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserQuery&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="s"&gt;"/user/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userDataAccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line of the code creates a data access module for the user table, and the second line builds a web access module based on the created data access module for the user table. The web access module handles requests at &lt;a href="http://localhost:9090/user/" rel="noopener noreferrer"&gt;http://localhost:9090/user/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Demo
&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;a href="https://github.com/doytowin/goooqo" rel="noopener noreferrer"&gt;demo&lt;/a&gt; repository locally and run the main method in the file &lt;a href="https://github.com/doytowin/goooqo-demo/blob/main/demo.go" rel="noopener noreferrer"&gt;demo.go&lt;/a&gt; to start the program, and then access the following URLs to view the response:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/" rel="noopener noreferrer"&gt;http://localhost:9090/user/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/3" rel="noopener noreferrer"&gt;http://localhost:9090/user/3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?pageNumber=2&amp;amp;pageSize=2" rel="noopener noreferrer"&gt;http://localhost:9090/user/?pageNumber=2&amp;amp;pageSize=2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?sort=id,desc" rel="noopener noreferrer"&gt;http://localhost:9090/user/?sort=id,desc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?sort=memo,desc%3Bemail,desc" rel="noopener noreferrer"&gt;http://localhost:9090/user/?sort=memo,desc%3Bemail,desc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?idIn=1,3" rel="noopener noreferrer"&gt;http://localhost:9090/user/?idIn=1,3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?emailContain=qq" rel="noopener noreferrer"&gt;http://localhost:9090/user/?emailContain=qq&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?emailContain=qq&amp;amp;IdGt=2" rel="noopener noreferrer"&gt;http://localhost:9090/user/?emailContain=qq&amp;amp;IdGt=2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?emailContain=qq&amp;amp;memoNull=true" rel="noopener noreferrer"&gt;http://localhost:9090/user/?emailContain=qq&amp;amp;memoNull=true&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:9090/user/?emailContain=qq&amp;amp;memoNull=false" rel="noopener noreferrer"&gt;http://localhost:9090/user/?emailContain=qq&amp;amp;memoNull=false&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the query string &lt;code&gt;emailContain=qq&amp;amp;memoNull=false&lt;/code&gt; will generate this SQL statement &lt;code&gt;SELECT * FROM t_user WHERE email LIKE '%qq' AND memo IS NOT NULL&lt;/code&gt; and get a response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test3@qq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mobile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"17778888883"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"nickname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"memo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"memo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also some sample HTTP requests provided in the file &lt;a href="https://github.com/doytowin/goooqo-demo/blob/main/user.http" rel="noopener noreferrer"&gt;user.http&lt;/a&gt; to test &lt;code&gt;POST/PUT/PATCH/DELETE&lt;/code&gt; operations. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;### Create new users
POST http://localhost:9090/user/
Content-Type: application/json

[{
  "Username": "Ada Wong",
  "Email": "AdaW@gmail.com",
  "Mobile": "01066666",
  "Nickname": "ada",
  "Memo": "An agent.",
  "Valid": true
}, {
  "Username": "Leon Kennedy",
  "Email": "LeonKennedy@gmail.com",
  "Mobile": "01077777",
  "Nickname": "leon",
  "Memo": "The hero.",
  "Valid": true
}]

### Update specified fields for the user 3

PATCH http://localhost:9090/user/3
Content-Type: application/json

{
  "Username": "Leon Kennedy",
  "Email": "LeonKennedy@gmail.com"
}

### Delete users where the memo field is not null
DELETE http://localhost:9090/user/?memoNull=false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Epilogue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/doytowin/goooqo" rel="noopener noreferrer"&gt;GoooQo&lt;/a&gt; is the Golang version of OQM technology following the Java version framework &lt;a href="http://github.com/doytowin/doyto-query" rel="noopener noreferrer"&gt;DoytoQuery&lt;/a&gt;. Currently, it is only an MVP that has undergone preliminary evaluation, and it will take multiple rounds of iterations to implement all the functions proposed by OQM technology. Please stay tuned and support us!&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: The Suffix Mapping Table
&lt;/h2&gt;

&lt;p&gt;The suffixes supported by version v0.1.3 and the corresponding query conditions can be found in this suffix mapping table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;后缀名称&lt;/th&gt;
&lt;th&gt;字段名称&lt;/th&gt;
&lt;th&gt;字段赋值&lt;/th&gt;
&lt;th&gt;SQL查询条件&lt;/th&gt;
&lt;th&gt;MongoDB查询条件&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(EMPTY)&lt;/td&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id = 5&lt;/td&gt;
&lt;td&gt;{"id":5}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eq&lt;/td&gt;
&lt;td&gt;idEq&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id = 5&lt;/td&gt;
&lt;td&gt;{"idEq":5}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not&lt;/td&gt;
&lt;td&gt;idNot&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id != 5&lt;/td&gt;
&lt;td&gt;{"idNot":{"$ne":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ne&lt;/td&gt;
&lt;td&gt;idNe&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id &amp;lt;&amp;gt; 5&lt;/td&gt;
&lt;td&gt;{"idNe":{"$ne":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gt&lt;/td&gt;
&lt;td&gt;idGt&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id &amp;gt; 5&lt;/td&gt;
&lt;td&gt;{"idGt":{"$gt":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ge&lt;/td&gt;
&lt;td&gt;idGe&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id &amp;gt;= 5&lt;/td&gt;
&lt;td&gt;{"idGe":{"$gte":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lt&lt;/td&gt;
&lt;td&gt;idLt&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id &amp;lt; 5&lt;/td&gt;
&lt;td&gt;{"idLt":{"$lt":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Le&lt;/td&gt;
&lt;td&gt;idLe&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;id &amp;lt;= 5&lt;/td&gt;
&lt;td&gt;{"idLe":{"$lte":5}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NotIn&lt;/td&gt;
&lt;td&gt;idNotIn&lt;/td&gt;
&lt;td&gt;[1,2,3]&lt;/td&gt;
&lt;td&gt;id NOT IN (1,2,3)&lt;/td&gt;
&lt;td&gt;{"id":{"$nin":[1, 2, 3]}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In&lt;/td&gt;
&lt;td&gt;idIn&lt;/td&gt;
&lt;td&gt;[1,2,3]&lt;/td&gt;
&lt;td&gt;id IN (1,2,3)&lt;/td&gt;
&lt;td&gt;{"id":{"$in":[1, 2, 3]}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Null&lt;/td&gt;
&lt;td&gt;memoNull&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;memo IS NOT NULL&lt;/td&gt;
&lt;td&gt;{"memo":{"$not":{"$type", 10}}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Null&lt;/td&gt;
&lt;td&gt;memoNull&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;memo IS NULL&lt;/td&gt;
&lt;td&gt;{"memo":{"$type", 10}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NotLike&lt;/td&gt;
&lt;td&gt;nameNotLike&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name NOT LIKE '%arg%'&lt;/td&gt;
&lt;td&gt;{"name":{"$not":{"$regex":"arg"}}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Like&lt;/td&gt;
&lt;td&gt;nameLike&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name LIKE '%arg%'&lt;/td&gt;
&lt;td&gt;{"name":{"$regex":"arg"}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NotStart&lt;/td&gt;
&lt;td&gt;nameNotStart&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name NOT LIKE 'arg%'&lt;/td&gt;
&lt;td&gt;{"name":{"$not":{"$regex":"^arg"}}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start&lt;/td&gt;
&lt;td&gt;nameStart&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name LIKE 'arg%'&lt;/td&gt;
&lt;td&gt;{"name":{"$regex":"^arg"}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NotEnd&lt;/td&gt;
&lt;td&gt;nameNotEnd&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name NOT LIKE '%arg'&lt;/td&gt;
&lt;td&gt;{"name":{"$not":{"$regex":"arg$"}}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End&lt;/td&gt;
&lt;td&gt;nameEnd&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name LIKE '%arg'&lt;/td&gt;
&lt;td&gt;{"name":{"$regex":"arg$"}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NotContain&lt;/td&gt;
&lt;td&gt;nameNotContain&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name NOT LIKE '%arg%’&lt;/td&gt;
&lt;td&gt;{"name":{"$not":{"$regex":"arg"}}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contain&lt;/td&gt;
&lt;td&gt;nameContain&lt;/td&gt;
&lt;td&gt;"arg"&lt;/td&gt;
&lt;td&gt;name LIKE '%arg%’&lt;/td&gt;
&lt;td&gt;{"name":{"$regex":"arg"}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rx&lt;/td&gt;
&lt;td&gt;nameRx&lt;/td&gt;
&lt;td&gt;"arg\d"&lt;/td&gt;
&lt;td&gt;name REGEXP 'arg\d’&lt;/td&gt;
&lt;td&gt;{"name":{"$regex":"arg\d"}}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Origin link: &lt;a href="https://blog.doyto.win/post/introduction-to-goooqo-en/" rel="noopener noreferrer"&gt;https://blog.doyto.win/post/introduction-to-goooqo-en/&lt;/a&gt;&lt;br&gt;
© 2024 Yuan Zhen. All rights reserved.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Express SELECT * FROM t WHERE id = ? OR (name = ? AND age = ?) by Objects Only?</title>
      <dc:creator>Forb Yuan</dc:creator>
      <pubDate>Sat, 24 Aug 2024 00:03:22 +0000</pubDate>
      <link>https://dev.to/f0rb/how-to-express-select-from-t-where-id-or-name-and-age-by-objects-only-3igb</link>
      <guid>https://dev.to/f0rb/how-to-express-select-from-t-where-id-or-name-and-age-by-objects-only-3igb</guid>
      <description>&lt;p&gt;To answer this question, we need to understand a new concept first: the WHERE clause is not flat but hierarchical. Within this structure, query conditions connected by AND form one level, and query conditions connected by OR form another. The logical connector for each layer is either AND or OR. Typically, multiple query conditions are connected using AND, which is considered the first level by default. Therefore, in the query statement mentioned in the title, the two conditions connected by OR are at the second level, and the two conditions within the second OR condition connected by AND are at the third level.&lt;/p&gt;

&lt;p&gt;Using JSON to represent this hierarchical structure might make it clearer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"userOr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"userAnd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When converting this JSON structure into a WHERE clause, we can determine the logical connectors between multiple query conditions based on the suffixes And/Or in the key names. Since there's only one key, &lt;code&gt;userOr&lt;/code&gt;, at the first level, the AND connector can be omitted. The multiple conditions under &lt;code&gt;userOr&lt;/code&gt; are connected using OR. The first key corresponds to the condition &lt;code&gt;id = 5&lt;/code&gt;, while the multiple conditions of the second key, &lt;code&gt;name = "John"&lt;/code&gt; and &lt;code&gt;age = 30&lt;/code&gt;, are connected with AND according to the suffix &lt;code&gt;And&lt;/code&gt;. These conditions are then enclosed in parentheses and combined with &lt;code&gt;id = 5&lt;/code&gt; using OR, resulting in the corresponding query clause: &lt;code&gt;id = 5 OR (name = "John" AND age = 30)&lt;/code&gt;. When constructing query conditions, the parameters can be replaced with placeholders.&lt;/p&gt;

&lt;p&gt;Next, let's try to construct such a JSON object using Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Getter&lt;/span&gt;
&lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;userOr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;userAnd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;userAnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;userOr&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;userAnd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userAnd&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;userQuery&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;UserQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;userOr&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userOr&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setSerializationInclusion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonInclude&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Include&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NON_NULL&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userQuery&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Output: {"userOr":{"id":5,"userAnd":{"name":"John","age":30}}}&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this approach may be a bit cumbersome, it allows us to construct the same JSON object, which means we can generate the same query clause. Furthermore, since this query condition can be represented in JSON format, it implies that any programming language supporting JSON can be used to construct such query conditions. The next step is to attempt to implement this solution in different programming languages.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>From ORM to OQM: An Object-Only SQL Construction Solution</title>
      <dc:creator>Forb Yuan</dc:creator>
      <pubDate>Fri, 23 Aug 2024 13:58:30 +0000</pubDate>
      <link>https://dev.to/f0rb/from-orm-to-oqm-an-object-only-sql-construction-solution-2k5p</link>
      <guid>https://dev.to/f0rb/from-orm-to-oqm-an-object-only-sql-construction-solution-2k5p</guid>
      <description>&lt;p&gt;Object/Relational Mapping (ORM) may lead the development of object-oriented applications based on relational databases in the wrong direction. A good technical solution should make things simpler rather than more complicated.&lt;/p&gt;

&lt;p&gt;There are actually only two steps to perform relational database access: SQL construction and SQL execution. ORM frameworks support SQL execution well, for example, Spring JDBC is a typical SQL execution tool, however, we still need to handwrite SQL or SQL construction code wrapped in any format for complex query statements. What we need is a completely automated SQL construction solution.&lt;/p&gt;

&lt;p&gt;Nowadays, most ORM frameworks can resolve the table name and column names through an entity object, and then build basic CRUD statements for a single table, while constructing the query clause requires users to provide several corresponding query parameters. According to the principle of high cohesion, we can define all query parameters of each table in one object, called a query object. Since each field in the query object can be built as a query condition, we can build a complete query clause through such a query object. Among them, these query parameters also include sorting parameters and paging parameters. In this way, the entity object and the query object constitute a pair of orthogonal objects for building CRUD statements for a single table.&lt;/p&gt;

&lt;p&gt;In addition to single-table CRUD statements, SQL construction also includes complex query statements. However, entity objects cannot be used to build complex query statements, because the fields in an entity object correspond to the columns in a single table, and the columns in a complex query statement may come from multiple tables or contain expressions, so we need to design another object to build static parts such as aggregate expressions, grouping clauses, and join relationships, which I call it a view object. At the same time, we can reuse query objects to build query clauses in complex query statements. Theoretically, these three types of objects can implement the automatic construction of SQL statements. The following figure shows an example of the Java implementation. The SQL statement on the right is the third query statement of the TPC-H benchmark test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsitk2sv19vginfdqam6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsitk2sv19vginfdqam6.jpg" alt="Image description" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article shares the idea of ​​automatically building SQL statements from objects only. I call this technology Object/Query-Language Mapping(OQM). In my next article, I will introduce the query object mapping method in detail.&lt;/p&gt;




&lt;p&gt;© 2024 Yuan Zhen. All rights reserved.&lt;br&gt;
Origin link: &lt;a href="https://blog.doyto.win/post/from-orm-to-oqm-en/" rel="noopener noreferrer"&gt;https://blog.doyto.win/post/from-orm-to-oqm-en/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
