├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── cla.yaml
│ ├── codeql.yml
│ ├── go.yml
│ └── license.yaml
├── .gitignore
├── .licenserc.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── builder
└── sql.go
├── cache
└── metadata_cache.go
├── common
├── define.go
└── result.go
├── connection
├── connection.go
└── default_connection.go
├── constants.go
├── datasource
├── common_datasource.go
├── datasource.go
├── mysql_datasource.go
├── postgre_datasource.go
└── sqlite_datasource.go
├── errors
└── errcode.go
├── executor
├── executor.go
├── prepare_executor.go
└── simple_executor.go
├── faccreator.go
├── factory
├── default_factory.go
├── factory.go
└── multisource.go
├── go.mod
├── go.sum
├── init.go
├── logging
└── logger.go
├── objectmanager.go
├── parsing
├── dynamics.go
├── sqlparser
│ └── parse.go
├── template
│ ├── common_v2.go
│ ├── dynamic.go
│ ├── parse.go
│ └── string_test.go
└── xml
│ ├── actions.go
│ ├── dynamics.go
│ ├── manager.go
│ ├── parse.go
│ ├── resultmap.go
│ └── root.go
├── reflection
├── object.go
├── parseparam.go
├── type.go
└── utils.go
├── session
├── default_session.go
├── propagation.go
└── sqlsession.go
├── sqlmanager.go
├── sqlrunner.go
├── statement
└── statement.go
├── test
├── cmd
│ ├── pg_cmd_test.go
│ ├── template
│ │ └── test_table_mapper.tmpl
│ ├── test_table.go
│ ├── test_table_proxy.go
│ └── xml
│ │ └── test_table_mapper.xml
├── mysql
│ └── runner_test.go
├── object_test.go
├── postgresql
│ ├── postgresql.tpl
│ ├── postgresql.xml
│ ├── postgresql2.tpl
│ ├── runner_test.go
│ └── session_test.go
├── reflection_test.go
├── runner_test.go
├── sql_test.go
├── sqlbuilder_test.go
├── sqlite
│ └── runner_test.go
├── sqlmanager
│ ├── manager_test.go
│ └── x
│ │ ├── tpl
│ │ └── x.tpl
│ │ └── xml
│ │ └── x.xml
├── sqlparse_test.go
├── template
│ ├── sql.tpl
│ ├── sql_v2.tpl
│ ├── temp_test.go
│ └── temp_v2_test.go
├── xml_file.go
└── xmlparse_test.go
├── transaction
├── default_transaction.go
└── transaction.go
└── util
└── rowutil.go
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/cla.yaml:
--------------------------------------------------------------------------------
1 | name: "CLA Assistant"
2 | on:
3 | issue_comment:
4 | types: [created]
5 | pull_request_target:
6 | types: [opened,closed,synchronize]
7 |
8 | jobs:
9 | CLAssistant:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: "CLA Assistant"
13 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
14 | # Beta Release
15 | uses: cla-assistant/github-action@v2.1.3-beta
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 | # the below token should have repo scope and must be manually added by you in the repository's secret
19 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
20 | with:
21 | path-to-signatures: 'signatures/version1/cla.json'
22 | path-to-document: 'https://github.com/acmestack/.github/blob/main/AcmeStack-CLA.md' # e.g. a CLA or a DCO document
23 | # branch should not be protected
24 | branch: 'cla'
25 | allowlist: bot*
26 |
27 | #below are the optional inputs - If the optional inputs are not given, then default values will be taken
28 | #remote-organization-name: acmestack #enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository)
29 | #remote-repository-name: cla # enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository)
30 | #create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
31 | #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
32 | #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
33 | #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
34 | #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
35 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
36 | #use-dco-flag: true - If you are using DCO instead of CLA
37 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '21 12 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'go' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 |
52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53 | # queries: security-extended,security-and-quality
54 |
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63 |
64 | # If the Autobuild fails above, remove it and uncomment the following three lines.
65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
66 |
67 | # - run: |
68 | # echo "Run, Build Application using script"
69 | # ./location_of_script_within_repo/buildscript.sh
70 |
71 | - name: Perform CodeQL Analysis
72 | uses: github/codeql-action/analyze@v2
73 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 |
11 | build:
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | db: ['MySQL', 'PostgreSQL']
16 | services:
17 | mysql:
18 | # Docker Hub image
19 | image: mysql:8
20 | env:
21 | MYSQL_ROOT_PASSWORD: test
22 | MYSQL_DATABASE: test
23 | MYSQL_USER: test
24 | MYSQL_PASSWORD: test
25 | # Set health checks to wait until mysql has started
26 | options: >-
27 | --health-cmd="mysqladmin ping"
28 | --health-interval 10s
29 | --health-timeout 5s
30 | --health-retries 5
31 | ports:
32 | - 3306:3306
33 | postgres:
34 | # Docker Hub image
35 | image: postgres:13.0
36 | env:
37 | POSTGRES_DB: testdb
38 | POSTGRES_USER: test
39 | POSTGRES_PASSWORD: test
40 | # Set health checks to wait until postgres has started
41 | options: >-
42 | --health-cmd pg_isready
43 | --health-interval 10s
44 | --health-timeout 5s
45 | --health-retries 5
46 | ports:
47 | - 5432:5432
48 | steps:
49 | - uses: actions/checkout@v3
50 |
51 | - name: Set up Go
52 | uses: actions/setup-go@v3
53 | with:
54 | go-version: 1.17
55 |
56 | - name: Build
57 | run: go build -v ./...
58 |
59 | #- name: Test
60 | # run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
61 |
62 | - uses: codecov/codecov-action@v2
63 |
--------------------------------------------------------------------------------
/.github/workflows/license.yaml:
--------------------------------------------------------------------------------
1 | name: License Checking
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | license:
13 | name: License Checking
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@master
17 | - name: Check License Lines
18 | uses: kt3k/license_checker@v1.0.6
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | *.zip
15 | /.idea/
16 | *.db
17 | vendor/
18 |
--------------------------------------------------------------------------------
/.licenserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "**/*.go" :[
3 | "/*",
4 | "* Licensed to the AcmeStack under one or more contributor license",
5 | "* agreements. See the NOTICE file distributed with this work for",
6 | "* additional information regarding copyright ownership.",
7 | "* Licensed under the Apache License, Version 2.0 (the \"License\");",
8 | "* you may not use this file except in compliance with the License.",
9 | "* You may obtain a copy of the License at",
10 | "*",
11 | "* http://www.apache.org/licenses/LICENSE-2.0",
12 | "*",
13 | "* Unless required by applicable law or agreed to in writing, software",
14 | "* distributed under the License is distributed on an \"AS IS\" BASIS,",
15 | "* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
16 | "* See the License for the specific language governing permissions and",
17 | "* limitations under the License.",
18 | "*/"
19 |
20 | ]
21 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | conduct@openingo.org.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | https://acmestack.org/docs/contributing/guide/
2 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 - 2022, AcmeStack
2 | All rights reserved.
3 |
4 | This product includes software developed at
5 | The AcmeStack (https://www.acmestack.org/).
6 |
--------------------------------------------------------------------------------
/cache/metadata_cache.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package cache
19 |
20 | import (
21 | "fmt"
22 | "github.com/acmestack/gobatis/parsing/sqlparser"
23 | "sort"
24 | "strings"
25 | "sync"
26 | )
27 |
28 | type MetadataCache struct {
29 | cache map[MetadataCacheKey]*sqlparser.Metadata
30 | lock sync.Mutex
31 | }
32 |
33 | type MetadataCacheKey string
34 |
35 | var gMetadataCache = MetadataCache{
36 | cache: map[MetadataCacheKey]*sqlparser.Metadata{},
37 | }
38 |
39 | func FindMetadata(key MetadataCacheKey) *sqlparser.Metadata {
40 | gMetadataCache.lock.Lock()
41 | defer gMetadataCache.lock.Unlock()
42 |
43 | return gMetadataCache.cache[key]
44 | }
45 |
46 | func CacheMetadata(key MetadataCacheKey, data *sqlparser.Metadata) {
47 | gMetadataCache.lock.Lock()
48 | defer gMetadataCache.lock.Unlock()
49 |
50 | gMetadataCache.cache[key] = data
51 | }
52 |
53 | func CalcKey(sql string, params map[string]interface{}) MetadataCacheKey {
54 | buf := strings.Builder{}
55 | buf.WriteString(sql)
56 | list := make([]string, len(params))
57 | i := 0
58 | for k := range params {
59 | list[i] = k
60 | i++
61 | }
62 | sort.Slice(list, func(i, j int) bool {
63 | return list[i] > list[j]
64 | })
65 | for i := range list {
66 | buf.WriteString(list[i])
67 | buf.WriteString(fmt.Sprintf("%v", params[list[i]]))
68 | }
69 | return MetadataCacheKey(buf.String())
70 | }
71 |
--------------------------------------------------------------------------------
/common/define.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package common
19 |
20 | // IterFunc 参数
21 | // idx:迭代数
22 | // bean:序列化后的值
23 | //返回值:
24 | // 打断迭代返回true
25 | type IterFunc func(idx int64, bean interface{}) bool
26 |
27 | const (
28 | ColumnName = "column"
29 | )
30 |
--------------------------------------------------------------------------------
/common/result.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package common
19 |
20 | type Result interface {
21 | LastInsertId() (int64, error)
22 | RowsAffected() (int64, error)
23 | }
24 |
--------------------------------------------------------------------------------
/connection/connection.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package connection
19 |
20 | import (
21 | "context"
22 | "github.com/acmestack/gobatis/common"
23 | "github.com/acmestack/gobatis/reflection"
24 | "github.com/acmestack/gobatis/statement"
25 | )
26 |
27 | type Connection interface {
28 | Prepare(sql string) (statement.Statement, error)
29 | Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error
30 | Exec(ctx context.Context, sql string, params ...interface{}) (common.Result, error)
31 | }
32 |
--------------------------------------------------------------------------------
/connection/default_connection.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package connection
19 |
20 | import (
21 | "context"
22 | "database/sql"
23 |
24 | "github.com/acmestack/gobatis/common"
25 | "github.com/acmestack/gobatis/errors"
26 | "github.com/acmestack/gobatis/reflection"
27 | "github.com/acmestack/gobatis/statement"
28 | "github.com/acmestack/gobatis/util"
29 | )
30 |
31 | type DefaultConnection sql.DB
32 | type DefaultStatement sql.Stmt
33 |
34 | func (conn *DefaultConnection) Prepare(sqlStr string) (statement.Statement, error) {
35 | db := (*sql.DB)(conn)
36 | s, err := db.Prepare(sqlStr)
37 | if err != nil {
38 | return nil, errors.ConnectionPrepareError
39 | }
40 | return (*DefaultStatement)(s), nil
41 | }
42 |
43 | func (conn *DefaultConnection) Query(ctx context.Context, result reflection.Object, sqlStr string, params ...interface{}) error {
44 | db := (*sql.DB)(conn)
45 | rows, err := db.QueryContext(ctx, sqlStr, params...)
46 | if err != nil {
47 | return errors.StatementQueryError
48 | }
49 | defer rows.Close()
50 |
51 | util.ScanRows(rows, result)
52 | return nil
53 | }
54 |
55 | func (conn *DefaultConnection) Exec(ctx context.Context, sqlStr string, params ...interface{}) (common.Result, error) {
56 | db := (*sql.DB)(conn)
57 | return db.ExecContext(ctx, sqlStr, params...)
58 | }
59 |
60 | func (s *DefaultStatement) Query(ctx context.Context, result reflection.Object, params ...interface{}) error {
61 | stmt := (*sql.Stmt)(s)
62 | rows, err := stmt.QueryContext(ctx, params...)
63 | if err != nil {
64 | return errors.StatementQueryError
65 | }
66 | defer rows.Close()
67 |
68 | util.ScanRows(rows, result)
69 | return nil
70 | }
71 |
72 | func (s *DefaultStatement) Exec(ctx context.Context, params ...interface{}) (common.Result, error) {
73 | stmt := (*sql.Stmt)(s)
74 | return stmt.ExecContext(ctx, params...)
75 | }
76 |
77 | func (s *DefaultStatement) Close() {
78 | stmt := (*sql.Stmt)(s)
79 | stmt.Close()
80 | }
81 |
--------------------------------------------------------------------------------
/constants.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package gobatis
19 |
20 | const (
21 | ContextSessionKey = "__gobatis_session__"
22 | )
23 |
--------------------------------------------------------------------------------
/datasource/common_datasource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package datasource
19 |
20 | type CommonDataSource struct {
21 | Name string
22 | Info string
23 | }
24 |
25 | func (ds *CommonDataSource) DriverName() string {
26 | return ds.Name
27 | }
28 |
29 | func (ds *CommonDataSource) DriverInfo() string {
30 | return ds.Info
31 | }
32 |
--------------------------------------------------------------------------------
/datasource/datasource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package datasource
19 |
20 | type DataSource interface {
21 | DriverName() string
22 | DriverInfo() string
23 | }
24 |
--------------------------------------------------------------------------------
/datasource/mysql_datasource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package datasource
19 |
20 | import "fmt"
21 |
22 | //import _ "github.com/go-sql-driver/mysql"
23 |
24 | type MysqlDataSource struct {
25 | Host string
26 | Port int
27 | DBName string
28 | Username string
29 | Password string
30 | Charset string
31 | }
32 |
33 | func (ds *MysqlDataSource) DriverName() string {
34 | return "mysql"
35 | }
36 |
37 | func (ds *MysqlDataSource) DriverInfo() string {
38 | return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", ds.Username, ds.Password, ds.Host, ds.Port, ds.DBName, ds.Charset)
39 | }
40 |
--------------------------------------------------------------------------------
/datasource/postgre_datasource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package datasource
19 |
20 | import "fmt"
21 |
22 | //import _ "github.com/lib/pq"
23 |
24 | type PostgreDataSource struct {
25 | Host string
26 | Port int
27 | DBName string
28 | Username string
29 | Password string
30 | SslMode string
31 | }
32 |
33 | func (ds *PostgreDataSource) DriverName() string {
34 | return "postgres"
35 | }
36 |
37 | func (ds *PostgreDataSource) DriverInfo() string {
38 | if ds.SslMode == "" {
39 | ds.SslMode = "disable"
40 | }
41 | return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", ds.Host, ds.Port, ds.Username, ds.Password, ds.DBName, ds.SslMode)
42 | }
43 |
--------------------------------------------------------------------------------
/datasource/sqlite_datasource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package datasource
19 |
20 | //import _ "github.com/mattn/go-sqlite3"
21 |
22 | type SqliteDataSource struct {
23 | Path string
24 | }
25 |
26 | func (ds *SqliteDataSource) DriverName() string {
27 | return "sqlite3"
28 | }
29 |
30 | func (ds *SqliteDataSource) DriverInfo() string {
31 | return ds.Path
32 | }
33 |
--------------------------------------------------------------------------------
/errors/errcode.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package errors
19 |
20 | import "fmt"
21 |
22 | type errCode struct {
23 | code string
24 | message string
25 | }
26 |
27 | var (
28 | FactoryInitialized = gobatisError("10002", "Factory have been initialized")
29 | ParseModelTableInfoFailed = gobatisError("11001", "Parse Model's table info failed")
30 | ModelNotRegister = gobatisError("11002", "Register model not found")
31 | ObjectNotSupport = gobatisError("11101", "Object not support")
32 | ParseObjectNotStruct = gobatisError("11102", "Parse interface's info but not a struct")
33 | ParseObjectNotSlice = gobatisError("11103", "Parse interface's info but not a slice")
34 | ParseObjectNotMap = gobatisError("11104", "Parse interface's info but not a map")
35 | ParseObjectNotSimpletype = gobatisError("11105", "Parse interface's info but not a simple type")
36 | SliceSliceNotSupport = gobatisError("11106", "Parse interface's info: [][]slice not support")
37 | GetObjectInfoFailed = gobatisError("11121", "Parse interface's info failed")
38 | SqlIdDuplicates = gobatisError("11205", "Sql id is duplicates")
39 | DeserializeFailed = gobatisError("11206", "Deserialize value failed")
40 | ParseSqlVarError = gobatisError("12001", "SQL PARSE ERROR")
41 | ParseSqlParamError = gobatisError("12002", "SQL PARSE parameter error")
42 | ParseSqlParamVarNumberError = gobatisError("12003", "SQL PARSE parameter var number error")
43 | ParseParserNilError = gobatisError("12004", "Dynamic sql parser is nil error")
44 | ParseDynamicSqlError = gobatisError("12010", "Parse dynamic sql error")
45 | ParseTemplateNilError = gobatisError("12101", "Parse template is nil")
46 | ExecutorCommitError = gobatisError("21001", "executor was closed when transaction commit")
47 | ExecutorBeginError = gobatisError("21002", "executor was closed when transaction begin")
48 | ExecutorQueryError = gobatisError("21003", "executor was closed when exec sql")
49 | ExecutorGetConnectionError = gobatisError("21003", "executor get connection error")
50 | TransactionWithoutBegin = gobatisError("22001", "Transaction without begin")
51 | TransactionCommitError = gobatisError("22002", "Transaction commit error")
52 | TransactionBusinessError = gobatisError("22003", "Business error in transaction")
53 | ConnectionPrepareError = gobatisError("23001", "Connection prepare error")
54 | StatementQueryError = gobatisError("24001", "statement query error")
55 | StatementExecError = gobatisError("24002", "statement exec error")
56 | QueryTypeError = gobatisError("25001", "select data convert error")
57 | ResultPointerIsNil = gobatisError("31000", "result type is a nil pointer")
58 | ResultIsnotPointer = gobatisError("31001", "result type is not pointer")
59 | ResultPtrValueIsPointer = gobatisError("31002", "result type is pointer of pointer")
60 | RunnerNotReady = gobatisError("31003", "Runner not ready, may sql or param have some error")
61 | ResultNameNotFound = gobatisError("31004", "result name not found")
62 | ResultSelectEmptyValue = gobatisError("31005", "select return empty value")
63 | ResultSetValueFailed = gobatisError("31006", "result set value failed")
64 | )
65 |
66 | func gobatisError(code, message string) errCode {
67 | return errCode{
68 | code: code,
69 | message: message,
70 | }
71 | }
72 |
73 | func (e errCode) Error() string {
74 | return fmt.Sprintf("%s - %s", e.code, e.message)
75 | }
76 |
--------------------------------------------------------------------------------
/executor/executor.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package executor
19 |
20 | import (
21 | "context"
22 | "github.com/acmestack/gobatis/common"
23 | "github.com/acmestack/gobatis/reflection"
24 | )
25 |
26 | type Executor interface {
27 | Close(rollback bool)
28 |
29 | Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error
30 |
31 | Exec(ctx context.Context, sql string, params ...interface{}) (common.Result, error)
32 |
33 | Begin() error
34 |
35 | Commit(require bool) error
36 |
37 | Rollback(require bool) error
38 | }
39 |
--------------------------------------------------------------------------------
/executor/prepare_executor.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package executor
19 |
20 | import (
21 | "context"
22 |
23 | "github.com/acmestack/gobatis/common"
24 | "github.com/acmestack/gobatis/errors"
25 | "github.com/acmestack/gobatis/reflection"
26 | "github.com/acmestack/gobatis/transaction"
27 | )
28 |
29 | type PrepareExecutor struct {
30 | transaction transaction.Transaction
31 | closed bool
32 | }
33 |
34 | func NewPrepareExecutor(transaction transaction.Transaction) *PrepareExecutor {
35 | return &PrepareExecutor{transaction: transaction}
36 | }
37 |
38 | func (exec *PrepareExecutor) Close(rollback bool) {
39 | defer func() {
40 | if exec.transaction != nil {
41 | exec.transaction.Close()
42 | }
43 | exec.transaction = nil
44 | exec.closed = true
45 | }()
46 |
47 | if rollback {
48 | exec.Rollback(true)
49 | }
50 | }
51 |
52 | func (exec *PrepareExecutor) Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error {
53 | if exec.closed {
54 | return errors.ExecutorQueryError
55 | }
56 |
57 | conn := exec.transaction.GetConnection()
58 | if conn == nil {
59 | return errors.ExecutorGetConnectionError
60 | }
61 |
62 | //FIXME: stmt must be close, use stmtCache instead
63 | stmt, err := conn.Prepare(sql)
64 | defer stmt.Close()
65 | if err != nil {
66 | return err
67 | }
68 | return stmt.Query(ctx, result, params...)
69 | }
70 |
71 | func (exec *PrepareExecutor) Exec(ctx context.Context, sql string, params ...interface{}) (common.Result, error) {
72 | if exec.closed {
73 | return nil, errors.ExecutorQueryError
74 | }
75 |
76 | conn := exec.transaction.GetConnection()
77 | if conn == nil {
78 | return nil, errors.ExecutorGetConnectionError
79 | }
80 |
81 | //FIXME: stmt must be close, use stmtCache instead
82 | stmt, err := conn.Prepare(sql)
83 | defer stmt.Close()
84 |
85 | if err != nil {
86 | return nil, err
87 | }
88 | return stmt.Exec(ctx, params...)
89 | }
90 |
91 | func (exec *PrepareExecutor) Begin() error {
92 | if exec.closed {
93 | return errors.ExecutorBeginError
94 | }
95 |
96 | return exec.transaction.Begin()
97 | }
98 |
99 | func (exec *PrepareExecutor) Commit(require bool) error {
100 | if exec.closed {
101 | return errors.ExecutorCommitError
102 | }
103 |
104 | if require {
105 | return exec.transaction.Commit()
106 | }
107 |
108 | return nil
109 | }
110 |
111 | func (exec *PrepareExecutor) Rollback(require bool) error {
112 | if !exec.closed {
113 | if require {
114 | return exec.transaction.Rollback()
115 | }
116 | }
117 | return nil
118 | }
119 |
--------------------------------------------------------------------------------
/executor/simple_executor.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package executor
19 |
20 | import (
21 | "context"
22 |
23 | "github.com/acmestack/gobatis/common"
24 | "github.com/acmestack/gobatis/errors"
25 | "github.com/acmestack/gobatis/reflection"
26 | "github.com/acmestack/gobatis/transaction"
27 | )
28 |
29 | type SimpleExecutor struct {
30 | transaction transaction.Transaction
31 | closed bool
32 | }
33 |
34 | func NewSimpleExecutor(transaction transaction.Transaction) *SimpleExecutor {
35 | return &SimpleExecutor{transaction: transaction}
36 | }
37 |
38 | func (exec *SimpleExecutor) Close(rollback bool) {
39 | defer func() {
40 | if exec.transaction != nil {
41 | exec.transaction.Close()
42 | }
43 | exec.transaction = nil
44 | exec.closed = true
45 | }()
46 |
47 | if rollback {
48 | exec.Rollback(true)
49 | }
50 | }
51 |
52 | func (exec *SimpleExecutor) Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error {
53 | if exec.closed {
54 | return errors.ExecutorQueryError
55 | }
56 |
57 | conn := exec.transaction.GetConnection()
58 | if conn == nil {
59 | return errors.ExecutorGetConnectionError
60 | }
61 |
62 | return conn.Query(ctx, result, sql, params...)
63 | }
64 |
65 | func (exec *SimpleExecutor) Exec(ctx context.Context, sql string, params ...interface{}) (common.Result, error) {
66 | if exec.closed {
67 | return nil, errors.ExecutorQueryError
68 | }
69 |
70 | conn := exec.transaction.GetConnection()
71 | if conn == nil {
72 | return nil, errors.ExecutorGetConnectionError
73 | }
74 |
75 | return conn.Exec(ctx, sql, params...)
76 | }
77 |
78 | func (exec *SimpleExecutor) Begin() error {
79 | if exec.closed {
80 | return errors.ExecutorBeginError
81 | }
82 |
83 | return exec.transaction.Begin()
84 | }
85 |
86 | func (exec *SimpleExecutor) Commit(require bool) error {
87 | if exec.closed {
88 | return errors.ExecutorCommitError
89 | }
90 |
91 | if require {
92 | return exec.transaction.Commit()
93 | }
94 |
95 | return nil
96 | }
97 |
98 | func (exec *SimpleExecutor) Rollback(require bool) error {
99 | if !exec.closed {
100 | if require {
101 | return exec.transaction.Rollback()
102 | }
103 | }
104 | return nil
105 | }
106 |
--------------------------------------------------------------------------------
/faccreator.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package gobatis
19 |
20 | import (
21 | "time"
22 |
23 | "github.com/acmestack/gobatis/datasource"
24 | "github.com/acmestack/gobatis/factory"
25 | "github.com/acmestack/gobatis/logging"
26 | )
27 |
28 | type FacOpt func(f *factory.DefaultFactory)
29 |
30 | func NewFactory(opts ...FacOpt) factory.Factory {
31 | f, _ := CreateFactory(opts...)
32 | return f
33 | }
34 |
35 | func CreateFactory(opts ...FacOpt) (factory.Factory, error) {
36 | f := &factory.DefaultFactory{
37 | Log: logging.DefaultLogf,
38 | }
39 |
40 | if len(opts) > 0 {
41 | for _, opt := range opts {
42 | opt(f)
43 | }
44 | }
45 |
46 | err := f.Open(f.DataSource)
47 | if err != nil {
48 | return nil, err
49 | }
50 |
51 | return f, nil
52 | }
53 |
54 | func SetMaxConn(maxConn int) FacOpt {
55 | return func(f *factory.DefaultFactory) {
56 | f.MaxConn = maxConn
57 | }
58 | }
59 |
60 | func SetMaxIdleConn(maxIdleConn int) FacOpt {
61 | return func(f *factory.DefaultFactory) {
62 | f.MaxIdleConn = maxIdleConn
63 | }
64 | }
65 |
66 | func SetConnMaxLifetime(v time.Duration) FacOpt {
67 | return func(f *factory.DefaultFactory) {
68 | f.ConnMaxLifetime = v
69 | }
70 | }
71 |
72 | func SetLog(logFunc logging.LogFunc) FacOpt {
73 | return func(f *factory.DefaultFactory) {
74 | f.Log = logFunc
75 | }
76 | }
77 |
78 | func SetDataSource(ds datasource.DataSource) FacOpt {
79 | return func(f *factory.DefaultFactory) {
80 | f.WithLock(func(fac *factory.DefaultFactory) {
81 | fac.DataSource = ds
82 | })
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/factory/default_factory.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package factory
19 |
20 | import (
21 | "database/sql"
22 | "sync"
23 | "time"
24 |
25 | "github.com/acmestack/gobatis/datasource"
26 | "github.com/acmestack/gobatis/errors"
27 | "github.com/acmestack/gobatis/executor"
28 | "github.com/acmestack/gobatis/logging"
29 | "github.com/acmestack/gobatis/session"
30 | "github.com/acmestack/gobatis/transaction"
31 | )
32 |
33 | type DefaultFactory struct {
34 | MaxConn int
35 | MaxIdleConn int
36 | ConnMaxLifetime time.Duration
37 | Log logging.LogFunc
38 |
39 | DataSource datasource.DataSource
40 |
41 | db *sql.DB
42 | mutex sync.Mutex
43 | }
44 |
45 | func (factory *DefaultFactory) Open(ds datasource.DataSource) error {
46 | factory.mutex.Lock()
47 | defer factory.mutex.Unlock()
48 |
49 | if factory.db != nil {
50 | return errors.FactoryInitialized
51 | }
52 |
53 | if ds != nil {
54 | factory.DataSource = ds
55 | }
56 |
57 | db, err := sql.Open(factory.DataSource.DriverName(), factory.DataSource.DriverInfo())
58 | if err != nil {
59 | return err
60 | }
61 |
62 | db.SetMaxOpenConns(factory.MaxConn)
63 | db.SetMaxIdleConns(factory.MaxIdleConn)
64 | db.SetConnMaxLifetime(factory.ConnMaxLifetime)
65 |
66 | factory.db = db
67 | return nil
68 | }
69 |
70 | func (factory *DefaultFactory) Close() error {
71 | if factory.db != nil {
72 | return factory.db.Close()
73 | }
74 | return nil
75 | }
76 |
77 | func (factory *DefaultFactory) GetDataSource() datasource.DataSource {
78 | return factory.DataSource
79 | }
80 |
81 | func (factory *DefaultFactory) CreateTransaction() transaction.Transaction {
82 | return transaction.NewDefaultTransaction(factory.DataSource, factory.db)
83 | }
84 |
85 | func (factory *DefaultFactory) CreateExecutor(transaction transaction.Transaction) executor.Executor {
86 | return executor.NewSimpleExecutor(transaction)
87 | }
88 |
89 | func (factory *DefaultFactory) CreateSession() session.SqlSession {
90 | tx := factory.CreateTransaction()
91 | return session.NewDefaultSqlSession(factory.Log, tx, factory.CreateExecutor(tx), false)
92 | }
93 |
94 | func (factory *DefaultFactory) LogFunc() logging.LogFunc {
95 | return factory.Log
96 | }
97 |
98 | func (factory *DefaultFactory) WithLock(lockFunc func(fac *DefaultFactory)) {
99 | factory.mutex.Lock()
100 | lockFunc(factory)
101 | factory.mutex.Unlock()
102 | }
103 |
104 | // Deprecated: Use Open instead
105 | func (factory *DefaultFactory) InitDB() error {
106 | return factory.Open(factory.DataSource)
107 | }
108 |
--------------------------------------------------------------------------------
/factory/factory.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package factory
19 |
20 | import (
21 | "github.com/acmestack/gobatis/datasource"
22 | "github.com/acmestack/gobatis/executor"
23 | "github.com/acmestack/gobatis/logging"
24 | "github.com/acmestack/gobatis/session"
25 | "github.com/acmestack/gobatis/transaction"
26 | )
27 |
28 | type Factory interface {
29 | Open(datasource.DataSource) error
30 | Close() error
31 |
32 | GetDataSource() datasource.DataSource
33 |
34 | CreateTransaction() transaction.Transaction
35 | CreateExecutor(transaction.Transaction) executor.Executor
36 | CreateSession() session.SqlSession
37 | LogFunc() logging.LogFunc
38 | }
39 |
--------------------------------------------------------------------------------
/factory/multisource.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package factory
19 |
20 | import (
21 | "github.com/xfali/loadbalance"
22 | )
23 |
24 | type LoadBalanceType int
25 |
26 | const (
27 | LBRoundRobbin LoadBalanceType = loadbalance.LBRoundRobbin
28 | LBRoundRobbinWeight LoadBalanceType = loadbalance.LBRoundRobbinWeight
29 | LBRandom LoadBalanceType = loadbalance.LBRandom
30 | LBRandomWeight LoadBalanceType = loadbalance.LBRandomWeight
31 |
32 | DefaultGroup = "default"
33 | )
34 |
35 | type Manager interface {
36 | Bind(action string, weight int, factory Factory)
37 | Select(action string) Factory
38 | }
39 |
40 | type SingleSource struct {
41 | fac Factory
42 | }
43 |
44 | func NewSingleSource(factory Factory) *SingleSource {
45 | return &SingleSource{fac: factory}
46 | }
47 |
48 | func (singleDs *SingleSource) Bind(action string, weight int, factory Factory) {
49 | singleDs.fac = factory
50 | }
51 |
52 | func (singleDs *SingleSource) Select(action string) Factory {
53 | return singleDs.fac
54 | }
55 |
56 | type DefaultMultiSource struct {
57 | lbType int
58 | actionMaps map[string]loadbalance.LoadBalance
59 | factoryMaps map[Factory]loadbalance.LoadBalance
60 | }
61 |
62 | func NewMultiSource(t LoadBalanceType) *DefaultMultiSource {
63 | return &DefaultMultiSource{
64 | actionMaps: map[string]loadbalance.LoadBalance{},
65 | factoryMaps: map[Factory]loadbalance.LoadBalance{},
66 | lbType: int(t),
67 | }
68 | }
69 |
70 | func (multiDs *DefaultMultiSource) Bind(action string, weight int, factory Factory) {
71 | if action == "" {
72 | action = DefaultGroup
73 | }
74 |
75 | if v, ok := multiDs.actionMaps[action]; ok {
76 | v.Add(weight, factory)
77 | } else {
78 | if f, ok := multiDs.factoryMaps[factory]; ok {
79 | multiDs.actionMaps[action] = f
80 | multiDs.factoryMaps[factory] = f
81 | } else {
82 | newlyMds := loadbalance.Create(multiDs.lbType)
83 | newlyMds.Add(weight, factory)
84 | multiDs.actionMaps[action] = newlyMds
85 | multiDs.factoryMaps[factory] = newlyMds
86 | }
87 | }
88 | }
89 |
90 | func (multiDs *DefaultMultiSource) Select(action string) Factory {
91 | if v, ok := multiDs.actionMaps[action]; ok {
92 | f := v.Select(nil)
93 | if f != nil {
94 | return f.(Factory)
95 | }
96 | }
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/acmestack/gobatis
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/Masterminds/sprig/v3 v3.2.2
7 | github.com/go-sql-driver/mysql v1.6.0
8 | github.com/lib/pq v1.10.6
9 | github.com/mattn/go-sqlite3 v1.14.13
10 | github.com/xfali/loadbalance v0.0.1
11 | )
12 |
13 | require (
14 | github.com/Masterminds/goutils v1.1.1 // indirect
15 | github.com/Masterminds/semver/v3 v3.1.1 // indirect
16 | github.com/google/uuid v1.1.1 // indirect
17 | github.com/huandu/xstrings v1.3.1 // indirect
18 | github.com/imdario/mergo v0.3.11 // indirect
19 | github.com/mitchellh/copystructure v1.0.0 // indirect
20 | github.com/mitchellh/reflectwalk v1.0.0 // indirect
21 | github.com/shopspring/decimal v1.2.0 // indirect
22 | github.com/spf13/cast v1.3.1 // indirect
23 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 // indirect
24 | )
25 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
2 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
3 | github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
4 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
5 | github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
6 | github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
11 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
12 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
13 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
14 | github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
15 | github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
16 | github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
17 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
18 | github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
19 | github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
20 | github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
21 | github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
22 | github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
23 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
24 | github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
25 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
26 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
27 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
28 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
29 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
30 | github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
31 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
33 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
34 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
35 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
36 | github.com/xfali/loadbalance v0.0.1 h1:UVOuuDipJ740KyTaBUxTeW6UEC+fukiQCOn/5YQgexA=
37 | github.com/xfali/loadbalance v0.0.1/go.mod h1:yrzHHRZMdt2wBpLnBDeU2zbjnRYeNvUWV6sq6+3KVG0=
38 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
39 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
40 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
41 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
42 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
43 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
44 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
45 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
46 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
47 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
48 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
49 |
--------------------------------------------------------------------------------
/init.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package gobatis
19 |
20 | import (
21 | "reflect"
22 |
23 | "github.com/acmestack/gobatis/reflection"
24 | )
25 |
26 | func init() {
27 | var typeModelName TableName
28 | reflection.SetModelNameType(reflect.TypeOf(typeModelName))
29 | }
30 |
--------------------------------------------------------------------------------
/logging/logger.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package logging
19 |
20 | import (
21 | "fmt"
22 | "log"
23 | "os"
24 | "runtime"
25 | )
26 |
27 | const (
28 | DEBUG = iota
29 | INFO
30 | WARN
31 | ERROR
32 | FATAL
33 | )
34 |
35 | var gLogTag = map[int]string{}
36 | var gLogLevel = INFO
37 |
38 | func init() {
39 | gLogTag[DEBUG] = "[Debug]"
40 | gLogTag[INFO] = "[Info]"
41 | gLogTag[WARN] = "[Warn]"
42 | gLogTag[ERROR] = "[Error]"
43 | gLogTag[FATAL] = "[Fatal]"
44 | }
45 |
46 | type LogFunc func(level int, format string, args ...interface{})
47 |
48 | var Log LogFunc = DefaultLogf
49 |
50 | func SetLevel(level int) {
51 | gLogLevel = level
52 | }
53 |
54 | func DummyLog(level int, format string, args ...interface{}) {
55 |
56 | }
57 |
58 | func DefaultLogf(level int, format string, args ...interface{}) {
59 | if gLogLevel > level {
60 | return
61 | }
62 |
63 | logInfo := fmt.Sprintf(format, args...)
64 | var file string
65 | var line int
66 | var ok bool
67 | _, file, line, ok = runtime.Caller(2)
68 | if !ok {
69 | file = "???"
70 | line = 0
71 | }
72 | log.Printf("%s %s:%d %s", gLogTag[level], shortFile(file), line, logInfo)
73 | if level >= FATAL {
74 | os.Exit(-1)
75 | }
76 | }
77 |
78 | func shortFile(file string) string {
79 | short := file
80 | for i := len(file) - 1; i > 0; i-- {
81 | if file[i] == '/' {
82 | short = file[i+1:]
83 | break
84 | }
85 | }
86 | return short
87 | }
88 |
89 | func Debug(format string, args ...interface{}) {
90 | Log(DEBUG, format, args...)
91 | }
92 |
93 | func Info(format string, args ...interface{}) {
94 | Log(INFO, format, args...)
95 | }
96 | func Warn(format string, args ...interface{}) {
97 | Log(WARN, format, args...)
98 | }
99 | func Error(format string, args ...interface{}) {
100 | Log(ERROR, format, args...)
101 | }
102 | func Fatal(format string, args ...interface{}) {
103 | Log(FATAL, format, args...)
104 | }
105 |
--------------------------------------------------------------------------------
/objectmanager.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package gobatis
19 |
20 | import (
21 | "sync"
22 |
23 | "github.com/acmestack/gobatis/errors"
24 | "github.com/acmestack/gobatis/reflection"
25 | )
26 |
27 | type TableName string
28 |
29 | type ObjectCache struct {
30 | objCache map[string]reflection.Object
31 | lock sync.Mutex
32 | }
33 |
34 | var globalObjectCache = ObjectCache{
35 | objCache: map[string]reflection.Object{},
36 | }
37 |
38 | func findObject(bean interface{}) reflection.Object {
39 | classname := reflection.GetBeanClassName(bean)
40 | globalObjectCache.lock.Lock()
41 | defer globalObjectCache.lock.Unlock()
42 |
43 | return globalObjectCache.objCache[classname]
44 | }
45 |
46 | func cacheObject(obj reflection.Object) {
47 | globalObjectCache.lock.Lock()
48 | defer globalObjectCache.lock.Unlock()
49 |
50 | globalObjectCache.objCache[obj.GetClassName()] = obj
51 | }
52 |
53 | func ParseObject(bean interface{}) (reflection.Object, error) {
54 | obj := findObject(bean)
55 | var err error
56 | if obj == nil {
57 | obj, err = reflection.GetObjectInfo(bean)
58 | if err != nil {
59 | return nil, err
60 | }
61 |
62 | cacheObject(obj)
63 | }
64 | obj = obj.New()
65 | obj.ResetValue(reflection.ReflectValue(bean))
66 | return obj, nil
67 | }
68 |
69 | // RegisterModel 注册struct模型,模型描述了column和field之间的关联关系;
70 | // 目前已非必要条件
71 | func RegisterModel(model interface{}) error {
72 | return RegisterModelWithName("", model)
73 | }
74 |
75 | func RegisterModelWithName(name string, model interface{}) error {
76 | tableInfo, err := reflection.GetObjectInfo(model)
77 | if err != nil {
78 | return errors.ParseModelTableInfoFailed
79 | }
80 |
81 | globalObjectCache.lock.Lock()
82 | defer globalObjectCache.lock.Unlock()
83 |
84 | if name == "" {
85 | name = tableInfo.GetClassName()
86 | }
87 | globalObjectCache.objCache[name] = tableInfo
88 | return nil
89 | }
90 |
--------------------------------------------------------------------------------
/parsing/dynamics.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package parsing
19 |
20 | import (
21 | "reflect"
22 | "strings"
23 | "time"
24 |
25 | "github.com/acmestack/gobatis/logging"
26 | "github.com/acmestack/gobatis/parsing/sqlparser"
27 | "github.com/acmestack/gobatis/reflection"
28 | )
29 |
30 | type GetFunc func(key string) string
31 |
32 | type DynamicElement interface {
33 | Format(func(key string) string) string
34 | }
35 |
36 | type DynamicData struct {
37 | OriginData string
38 | DynamicElemMap map[string]DynamicElement
39 | }
40 |
41 | func (dynamicData *DynamicData) Replace(params ...interface{}) string {
42 | objMap := reflection.ParseParams(params...)
43 | return dynamicData.ReplaceWithMap(objMap)
44 | }
45 |
46 | // ReplaceWithMap 需要外部确保param是一个struct
47 | func (dynamicData *DynamicData) ReplaceWithMap(objParams map[string]interface{}) string {
48 | if len(dynamicData.DynamicElemMap) == 0 || len(objParams) == 0 {
49 | logging.Info("map is empty")
50 | //return dynamicData.OriginData
51 | }
52 |
53 | getFunc := func(s string) string {
54 | if o, ok := objParams[s]; ok {
55 | if str, ok := o.(string); ok {
56 | return str
57 | }
58 |
59 | //zero time convert to empty string (for element)
60 | if ti, ok := o.(time.Time); ok {
61 | if ti.IsZero() {
62 | return ""
63 | } else {
64 | return ti.String()
65 | }
66 | }
67 |
68 | var str string
69 | reflection.SafeSetValue(reflect.ValueOf(&str), o)
70 | return str
71 | }
72 | return ""
73 | }
74 |
75 | ret := dynamicData.OriginData
76 | for k, v := range dynamicData.DynamicElemMap {
77 | ret = strings.Replace(ret, k, v.Format(getFunc), -1)
78 | }
79 | return ret
80 | }
81 |
82 | func (dynamicData *DynamicData) ParseMetadata(driverName string, params ...interface{}) (*sqlparser.Metadata, error) {
83 | paramMap := reflection.ParseParams(params...)
84 | sqlStr := dynamicData.ReplaceWithMap(paramMap)
85 | return sqlparser.ParseWithParamMap(driverName, sqlStr, paramMap)
86 | }
87 |
--------------------------------------------------------------------------------
/parsing/sqlparser/parse.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package sqlparser
19 |
20 | import (
21 | "fmt"
22 | "strconv"
23 | "strings"
24 | "unicode"
25 |
26 | "github.com/acmestack/gobatis/errors"
27 | )
28 |
29 | const (
30 | SELECT = "select"
31 | INSERT = "insert"
32 | UPDATE = "update"
33 | DELETE = "delete"
34 | )
35 |
36 | type Metadata struct {
37 | Action string
38 | PrepareSql string
39 | Vars []string
40 | Params []interface{}
41 | }
42 |
43 | type SqlParser interface {
44 | ParseMetadata(driverName string, params ...interface{}) (*Metadata, error)
45 | }
46 |
47 | func SimpleParse(sql string) (*Metadata, error) {
48 | ret := Metadata{}
49 | sql = strings.Trim(sql, " ")
50 | action := sql[:6]
51 | action = strings.ToLower(action)
52 | ret.Action = action
53 |
54 | subStr := sql
55 | firstIndex, lastIndex := -1, -1
56 | for {
57 | firstIndex = strings.Index(subStr, "#{")
58 | if firstIndex == -1 {
59 | break
60 | } else {
61 | subStr = subStr[firstIndex+2:]
62 | lastIndex = findFirst(subStr, '}')
63 | //lastIndex = strings.Index(subStr, "}")
64 | if lastIndex == -1 {
65 | return nil, errors.ParseSqlVarError
66 | } else {
67 | varName := subStr[:lastIndex]
68 | if varName != "" {
69 | ret.Vars = append(ret.Vars, varName)
70 | }
71 | }
72 | }
73 | subStr = subStr[lastIndex+1:]
74 | }
75 |
76 | ret.PrepareSql = sql
77 | for _, varName := range ret.Vars {
78 | ret.PrepareSql = strings.Replace(ret.PrepareSql, "#{"+varName+"}", "?", -1)
79 | }
80 |
81 | return &ret, nil
82 | }
83 |
84 | func ParseWithParams(sql string, params ...interface{}) (*Metadata, error) {
85 | ret := Metadata{}
86 | sql = strings.Trim(sql, " ")
87 | action := sql[:6]
88 | action = strings.ToLower(action)
89 | ret.Action = action
90 |
91 | ret.PrepareSql = sql
92 | subStr := sql
93 | firstIndex, lastIndex := -1, -1
94 | var c string
95 | for {
96 | firstIndex = strings.Index(subStr, "{")
97 | if firstIndex == -1 || firstIndex == 0 {
98 | break
99 | } else {
100 | c = subStr[firstIndex-1 : firstIndex]
101 | subStr = subStr[firstIndex+1:]
102 | lastIndex = findFirst(subStr, '}')
103 | //lastIndex = strings.Index(subStr, "}")
104 | if lastIndex == -1 {
105 | return nil, errors.ParseSqlVarError
106 | } else {
107 | varName := subStr[:lastIndex]
108 | if varName != "" {
109 | ret.Vars = append(ret.Vars, varName)
110 | indexV, err := strconv.Atoi(varName)
111 | if err != nil {
112 | return nil, errors.ParseSqlParamVarNumberError
113 | }
114 | if c == "$" {
115 | if len(params) <= indexV {
116 | return nil, errors.ParseSqlParamError
117 | }
118 | oldStr := "${" + varName + "}"
119 | newStr := interface2String(params[indexV])
120 | ret.PrepareSql = strings.Replace(ret.PrepareSql, oldStr, newStr, -1)
121 | subStr = strings.Replace(subStr, oldStr, newStr, -1)
122 | } else if c == "#" {
123 | if len(params) < indexV {
124 | return nil, errors.ParseSqlParamError
125 | }
126 | oldStr := "#{" + varName + "}"
127 | ret.PrepareSql = strings.Replace(ret.PrepareSql, oldStr, "?", -1)
128 | ret.Params = append(ret.Params, params[indexV])
129 | }
130 | }
131 | }
132 | }
133 | subStr = subStr[lastIndex+1:]
134 | }
135 |
136 | return &ret, nil
137 | }
138 |
139 | func ParseWithParamMap(driverName, sql string, params map[string]interface{}) (*Metadata, error) {
140 | ret := Metadata{}
141 | sql = strings.Trim(sql, " ")
142 | action := sql[:6]
143 | action = strings.ToLower(action)
144 | ret.Action = action
145 |
146 | ret.PrepareSql = sql
147 | subStr := sql
148 | firstIndex, lastIndex := -1, -1
149 | var c string
150 | var index int = 0
151 | holder := SelectMarker(driverName)
152 |
153 | for {
154 | firstIndex = strings.Index(subStr, "{")
155 | if firstIndex == -1 || firstIndex == 0 {
156 | break
157 | } else {
158 | c = subStr[firstIndex-1 : firstIndex]
159 | subStr = subStr[firstIndex+1:]
160 | lastIndex = findFirst(subStr, '}')
161 | //lastIndex = strings.Index(subStr, "}")
162 | if lastIndex == -1 {
163 | return nil, errors.ParseSqlVarError
164 | } else {
165 | varName := subStr[:lastIndex]
166 | if varName != "" {
167 | ret.Vars = append(ret.Vars, varName)
168 | if value, ok := params[varName]; ok {
169 | if c == "$" {
170 | oldStr := "${" + varName + "}"
171 | newStr := interface2String(value)
172 | ret.PrepareSql = strings.Replace(ret.PrepareSql, oldStr, newStr, -1)
173 | subStr = strings.Replace(subStr, oldStr, newStr, -1)
174 | } else if c == "#" {
175 | oldStr := "#{" + varName + "}"
176 | index++
177 | h := holder(index)
178 | ret.PrepareSql = strings.Replace(ret.PrepareSql, oldStr, h, -1)
179 | ret.Params = append(ret.Params, value)
180 | }
181 | } else {
182 | return nil, errors.ParseSqlParamError
183 | }
184 | }
185 | }
186 | }
187 | subStr = subStr[lastIndex+1:]
188 | }
189 |
190 | return &ret, nil
191 | }
192 |
193 | type Holder func(int) string
194 |
195 | var gHolderMap = map[string]Holder{
196 | "mysql": MysqlMarker, //mysql
197 | "postgres": PostgresMarker, //postgresql
198 | "oci8": Oci8Marker, //oracle
199 | "adodb": MysqlMarker, //sqlserver
200 | }
201 |
202 | func RegisterParamMarker(driverName string, h Holder) bool {
203 | _, ok := GetMarker(driverName)
204 | gHolderMap[driverName] = h
205 | return ok
206 | }
207 |
208 | func SelectMarker(driverName string) Holder {
209 | if v, ok := GetMarker(driverName); ok {
210 | return v
211 | }
212 | return MysqlMarker
213 | }
214 |
215 | func GetMarker(driverName string) (Holder, bool) {
216 | v, ok := gHolderMap[driverName]
217 | return v, ok
218 | }
219 |
220 | func MysqlMarker(int) string {
221 | return "?"
222 | }
223 |
224 | func PostgresMarker(i int) string {
225 | return "$" + strconv.Itoa(i)
226 | }
227 |
228 | func Oci8Marker(i int) string {
229 | return ":" + strconv.Itoa(i)
230 | }
231 |
232 | func interface2String(i interface{}) string {
233 | return fmt.Sprintf("%v", i)
234 | }
235 |
236 | func findFirst(subStr string, char rune) int {
237 | for i, r := range subStr {
238 | //switch r {
239 | //case ',', ' ', '\t', '\n', '\r':
240 | // return -1
241 | //case char:
242 | // return i
243 | //}
244 | if unicode.IsSpace(r) || r == ',' {
245 | return -1
246 | } else if r == char {
247 | return i
248 | }
249 | }
250 | return -1
251 | }
252 |
253 | func (md *Metadata) String() string {
254 | return fmt.Sprintf("action: %s, prepareSql: %s, varmap: %v, params: %v", md.Action, md.PrepareSql, md.Vars, md.Params)
255 | }
256 |
--------------------------------------------------------------------------------
/parsing/template/common_v2.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "fmt"
22 | "github.com/Masterminds/sprig/v3"
23 | "github.com/acmestack/gobatis/parsing/sqlparser"
24 | "strings"
25 | "text/template"
26 | )
27 |
28 | type CommonV2Dynamic struct {
29 | index int
30 | keys []string
31 | paramMap map[string]interface{}
32 | holder sqlparser.Holder
33 | }
34 |
35 | func (d *CommonV2Dynamic) getFuncMap() template.FuncMap {
36 | ret := sprig.TxtFuncMap()
37 | ret[FuncNameSet] = d.UpdateSet
38 | ret[FuncNameWhere] = d.Where
39 | ret[FuncNameArg] = d.Param
40 | ret[FuncNameAdd] = commonAdd
41 | return ret
42 | }
43 |
44 | func (d *CommonV2Dynamic) UpdateSet(segments ...interface{}) string {
45 | buf := strings.Builder{}
46 | if len(segments) > 0 {
47 | buf.WriteString(" SET ")
48 | } else {
49 | return ""
50 | }
51 | for _, value := range segments {
52 | if s, ok := value.(string); ok {
53 | if _, ok := d.paramMap[s]; ok {
54 | buf.WriteString(s)
55 | } else {
56 | buf.WriteString(`'`)
57 | buf.WriteString(s)
58 | buf.WriteString(`'`)
59 | }
60 | } else {
61 | buf.WriteString(fmt.Sprint(value))
62 | }
63 | }
64 |
65 | return buf.String()
66 | }
67 |
68 | func (d *CommonV2Dynamic) Where(segments ...interface{}) string {
69 | buf := strings.Builder{}
70 | if len(segments) > 0 {
71 | buf.WriteString(" WHERE ")
72 | } else {
73 | return ""
74 | }
75 | for _, value := range segments {
76 | if s, ok := value.(string); ok {
77 | if _, ok := d.paramMap[s]; ok {
78 | buf.WriteString(s)
79 | } else {
80 | buf.WriteString(`'`)
81 | buf.WriteString(s)
82 | buf.WriteString(`'`)
83 | }
84 | } else {
85 | buf.WriteString(fmt.Sprint(value))
86 | }
87 | }
88 |
89 | return buf.String()
90 | }
91 |
92 | func (d *CommonV2Dynamic) getParam() []interface{} {
93 | return nil
94 | }
95 |
96 | func (d *CommonV2Dynamic) Param(p interface{}) string {
97 | d.index++
98 | key := getPlaceHolderKey(d.index)
99 | d.paramMap[key] = p
100 | d.keys = append(d.keys, key)
101 | return key
102 | }
103 |
104 | func (d *CommonV2Dynamic) format(s string) (string, []interface{}) {
105 | i, index := 0, 1
106 | var params []interface{}
107 | for _, k := range d.keys {
108 | s, i = replace(s, k, d.holder(index), -1)
109 | if i > 0 {
110 | params = append(params, d.paramMap[k])
111 | index++
112 | }
113 | }
114 | return s, params
115 | }
116 |
117 | func CreateV2DynamicHandler(h sqlparser.Holder) Dynamic {
118 | return &CommonV2Dynamic{
119 | index: 0,
120 | keys: nil,
121 | paramMap: map[string]interface{}{},
122 | holder: h,
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/parsing/template/dynamic.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "fmt"
22 | "strings"
23 | "text/template"
24 | "time"
25 |
26 | "github.com/acmestack/gobatis/parsing/sqlparser"
27 | )
28 |
29 | const (
30 | argPlaceHolder = "_xfali_Arg_Holder"
31 | argPlaceHolderLen = 17
32 | argPlaceHolderFormat = "%s%08d"
33 |
34 | FuncNameSet = "set"
35 | FuncNameWhere = "where"
36 | FuncNameArg = "arg"
37 | FuncNameAdd = "add"
38 | )
39 |
40 | type Dynamic interface {
41 | getFuncMap() template.FuncMap
42 | format(string) (string, []interface{})
43 | }
44 |
45 | var ArgPlaceHolderFormat = argPlaceHolderFormat
46 |
47 | func dummyUpdateSet(b interface{}, column string, value interface{}, origin string) string {
48 | return origin
49 | }
50 |
51 | func dummyWhere(b interface{}, cond, column string, value interface{}, origin string) string {
52 | return origin
53 | }
54 |
55 | //return as fast as possible
56 | func dummyParam(p interface{}) string {
57 | return ""
58 | }
59 |
60 | func dummyNil(p interface{}) bool {
61 | return true
62 | }
63 |
64 | func commonAdd(a, b int) int {
65 | return a + b
66 | }
67 |
68 | type DummyDynamic struct{}
69 |
70 | var dummyFuncMap = template.FuncMap{
71 | FuncNameSet: dummyUpdateSet,
72 | FuncNameWhere: dummyWhere,
73 | FuncNameArg: dummyParam,
74 |
75 | FuncNameAdd: commonAdd,
76 | }
77 |
78 | var gDummyDynamic = &DummyDynamic{}
79 |
80 | func (dummyDynamic *DummyDynamic) getFuncMap() template.FuncMap {
81 | return dummyFuncMap
82 | }
83 |
84 | func (dummyDynamic *DummyDynamic) getParam() []interface{} {
85 | return nil
86 | }
87 |
88 | func (dummyDynamic *DummyDynamic) format(s string) (string, []interface{}) {
89 | return s, nil
90 | }
91 |
92 | type CommonDynamic struct {
93 | index int
94 | keys []string
95 | paramMap map[string]interface{}
96 | holder sqlparser.Holder
97 | }
98 |
99 | func CreateDynamicHandler(holder sqlparser.Holder) Dynamic {
100 | return &CommonDynamic{
101 | index: 0,
102 | keys: nil,
103 | paramMap: map[string]interface{}{},
104 | holder: holder,
105 | }
106 | }
107 |
108 | func (dynamic *CommonDynamic) getFuncMap() template.FuncMap {
109 | return template.FuncMap{
110 | FuncNameSet: dynamic.UpdateSet,
111 | FuncNameWhere: dynamic.Where,
112 | FuncNameArg: dynamic.Param,
113 |
114 | FuncNameAdd: commonAdd,
115 | }
116 | }
117 |
118 | func (dynamic *CommonDynamic) UpdateSet(b interface{}, columnDesc string, value interface{}, origin string) string {
119 | if !IsTrue(b) {
120 | return origin
121 | }
122 |
123 | buf := strings.Builder{}
124 | if origin == "" {
125 | buf.WriteString(" SET ")
126 | } else {
127 | origin = strings.TrimSpace(origin)
128 | buf.WriteString(origin)
129 | if origin[:len(origin)-1] != "," {
130 | buf.WriteString(",")
131 | }
132 | }
133 | buf.WriteString(columnDesc)
134 | if s, ok := value.(string); ok {
135 | if _, ok := dynamic.paramMap[s]; ok {
136 | buf.WriteString(s)
137 | } else {
138 | buf.WriteString(`'`)
139 | buf.WriteString(s)
140 | buf.WriteString(`'`)
141 | }
142 | } else {
143 | buf.WriteString(fmt.Sprint(value))
144 | }
145 | return buf.String()
146 | }
147 |
148 | func (dynamic *CommonDynamic) Where(b interface{}, cond, columnDesc string, value interface{}, origin string) string {
149 | if !IsTrue(b) {
150 | return origin
151 | }
152 |
153 | buf := strings.Builder{}
154 | if origin == "" {
155 | buf.WriteString(" WHERE ")
156 | cond = ""
157 | } else {
158 | buf.WriteString(strings.TrimSpace(origin))
159 | buf.WriteString(" ")
160 | buf.WriteString(cond)
161 | buf.WriteString(" ")
162 | }
163 |
164 | buf.WriteString(columnDesc)
165 | if s, ok := value.(string); ok {
166 | if _, ok := dynamic.paramMap[s]; ok {
167 | buf.WriteString(s)
168 | } else {
169 | buf.WriteString(`'`)
170 | buf.WriteString(s)
171 | buf.WriteString(`'`)
172 | }
173 | } else {
174 | buf.WriteString(fmt.Sprint(value))
175 | }
176 | return buf.String()
177 | }
178 |
179 | func (dynamic *CommonDynamic) getParam() []interface{} {
180 | return nil
181 | }
182 |
183 | func (dynamic *CommonDynamic) Param(p interface{}) string {
184 | dynamic.index++
185 | key := getPlaceHolderKey(dynamic.index)
186 | dynamic.paramMap[key] = p
187 | dynamic.keys = append(dynamic.keys, key)
188 | return key
189 | }
190 |
191 | func (dynamic *CommonDynamic) format(s string) (string, []interface{}) {
192 | i, index := 0, 1
193 | var params []interface{}
194 | for _, k := range dynamic.keys {
195 | s, i = replace(s, k, dynamic.holder(index), -1)
196 | if i > 0 {
197 | params = append(params, dynamic.paramMap[k])
198 | index++
199 | }
200 | }
201 | return s, params
202 | }
203 |
204 | func selectDynamic(driverName string) Dynamic {
205 | if h, ok := sqlparser.GetMarker(driverName); ok {
206 | return dynamicFac(h)
207 | }
208 | return gDummyDynamic
209 | }
210 |
211 | func replace(s, old, new string, n int) (string, int) {
212 | if old == new || n == 0 {
213 | return s, 0 // avoid allocation
214 | }
215 |
216 | if old == "" {
217 | return s, 0
218 | }
219 |
220 | if n < 0 {
221 | if m := strings.Count(s, old); m == 0 {
222 | return s, 0 // avoid allocation
223 | } else if n < 0 || m < n {
224 | n = m
225 | }
226 | }
227 | makeSize := len(s) + n*(len(new)-len(old))
228 | // Apply replacements to buffer.
229 | t := make([]byte, makeSize)
230 | w, count := 0, 0
231 | start := 0
232 | for {
233 | if n == 0 {
234 | break
235 | }
236 | j := start
237 | index := strings.Index(s[start:], old)
238 | if index == -1 {
239 | return string(t[0:w]), count
240 | } else {
241 | j += index
242 | count++
243 | }
244 | w += copy(t[w:], s[start:j])
245 | w += copy(t[w:], new)
246 | start = j + len(old)
247 | n--
248 | }
249 | w += copy(t[w:], s[start:])
250 | return string(t[0:w]), count
251 | }
252 |
253 | func IsTrue(i interface{}) bool {
254 | t, _ := template.IsTrue(i)
255 | if !t {
256 | return t
257 | }
258 |
259 | if ti, ok := i.(time.Time); ok {
260 | if ti.IsZero() {
261 | return false
262 | }
263 | }
264 | return t
265 | }
266 |
267 | func getPlaceHolderKey(index int) string {
268 | return fmt.Sprintf(ArgPlaceHolderFormat, argPlaceHolder, index)
269 | }
270 |
271 | var dynamicFac = CreateDynamicHandler
272 |
273 | func SetDynamicFactory(f func(h sqlparser.Holder) Dynamic) {
274 | dynamicFac = f
275 | }
276 |
--------------------------------------------------------------------------------
/parsing/template/parse.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "io/ioutil"
22 | "strings"
23 | "sync"
24 | "text/template"
25 |
26 | "github.com/acmestack/gobatis/errors"
27 | "github.com/acmestack/gobatis/logging"
28 | "github.com/acmestack/gobatis/parsing/sqlparser"
29 | )
30 |
31 | const (
32 | namespaceTmplName = "namespace"
33 | )
34 |
35 | type Parser struct {
36 | //template
37 | tpl *template.Template
38 | }
39 |
40 | func CreateParser(data []byte) (*Parser, error) {
41 | tpl := template.New("")
42 | tpl = tpl.Funcs(dummyFuncMap)
43 | tpl, err := tpl.Parse(string(data))
44 | if err != nil {
45 | return nil, err
46 | }
47 | return &Parser{tpl: tpl}, nil
48 | }
49 |
50 | // ParseMetadata only use first param
51 | func (p *Parser) ParseMetadata(driverName string, params ...interface{}) (*sqlparser.Metadata, error) {
52 | if p.tpl == nil {
53 | return nil, errors.ParseTemplateNilError
54 | }
55 | b := strings.Builder{}
56 | var param interface{} = nil
57 | if len(params) == 1 {
58 | param = params[0]
59 | } else {
60 | param = params
61 | }
62 | dynamic := selectDynamic(driverName)
63 | tpl := p.tpl.Funcs(dynamic.getFuncMap())
64 | err := tpl.Execute(&b, param)
65 | if err != nil {
66 | return nil, err
67 | }
68 |
69 | ret := &sqlparser.Metadata{}
70 | sql := strings.TrimSpace(b.String())
71 | action := sql[:6]
72 | action = strings.ToLower(action)
73 | ret.Action = action
74 | ret.PrepareSql, ret.Params = dynamic.format(sql)
75 |
76 | return ret, nil
77 | }
78 |
79 | type Manager struct {
80 | sqlMap map[string]*Parser
81 | lock sync.Mutex
82 | }
83 |
84 | func NewManager() *Manager {
85 | return &Manager{
86 | sqlMap: map[string]*Parser{},
87 | }
88 | }
89 |
90 | func (manager *Manager) RegisterData(data []byte) error {
91 | manager.lock.Lock()
92 | defer manager.lock.Unlock()
93 |
94 | tpl := template.New("")
95 | tpl = tpl.Funcs(dummyFuncMap)
96 | tpl, err := tpl.Parse(string(data))
97 | if err != nil {
98 | logging.Warn("register template data failed: %s err: %v\n", string(data), err)
99 | return err
100 | }
101 |
102 | ns := getNamespace(tpl)
103 | tpls := tpl.Templates()
104 | for _, v := range tpls {
105 | if v.Name() != "" && v.Name() != namespaceTmplName {
106 | manager.sqlMap[ns+v.Name()] = &Parser{tpl: v}
107 | }
108 | }
109 |
110 | return nil
111 | }
112 |
113 | func (manager *Manager) RegisterFile(file string) error {
114 | manager.lock.Lock()
115 | defer manager.lock.Unlock()
116 |
117 | tpl := template.New("")
118 | data, err := ioutil.ReadFile(file)
119 | if err != nil {
120 | logging.Warn("register template file failed: %s err: %v\n", file, err)
121 | return err
122 | }
123 | tpl = tpl.Funcs(dummyFuncMap)
124 | tpl, err = tpl.Parse(string(data))
125 | if err != nil {
126 | logging.Warn("register template file failed: %s err: %v\n", file, err)
127 | return err
128 | }
129 |
130 | ns := getNamespace(tpl)
131 | tpls := tpl.Templates()
132 | for _, v := range tpls {
133 | if v.Name() != "" && v.Name() != namespaceTmplName {
134 | manager.sqlMap[ns+v.Name()] = &Parser{tpl: v}
135 | }
136 | }
137 |
138 | return nil
139 | }
140 |
141 | func getNamespace(tpl *template.Template) string {
142 | ns := strings.Builder{}
143 | nsTpl := tpl.Lookup(namespaceTmplName)
144 | if nsTpl != nil {
145 | err := nsTpl.Execute(&ns, nil)
146 | if err != nil {
147 | ns.Reset()
148 | }
149 | }
150 |
151 | ret := strings.TrimSpace(ns.String())
152 |
153 | if ret != "" {
154 | ret = ret + "."
155 | }
156 | return ret
157 | }
158 |
159 | func (manager *Manager) FindSqlParser(sqlId string) (*Parser, bool) {
160 | manager.lock.Lock()
161 | defer manager.lock.Unlock()
162 |
163 | v, ok := manager.sqlMap[sqlId]
164 | return v, ok
165 | }
166 |
--------------------------------------------------------------------------------
/parsing/template/string_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "testing"
22 | )
23 |
24 | func TestReplace(t *testing.T) {
25 | old := "ab"
26 | newly := "cdab"
27 | s, i := replace("fhaksfjlabdasdabdasljfabda", old, newly, -1)
28 | t.Log(s, " ", i)
29 | }
30 |
31 | func TestFmt(t *testing.T) {
32 | s := getPlaceHolderKey(10)
33 | t.Log(s)
34 | }
35 |
--------------------------------------------------------------------------------
/parsing/xml/actions.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package xml
19 |
20 | import "encoding/xml"
21 |
22 | type Select struct {
23 | XMLName xml.Name
24 | Id string `xml:"id,attr"`
25 | ParameterType string `xml:"parameterType,attr"`
26 | ParameterMap string `xml:"parameterMap,attr"`
27 | ResultType string `xml:"resultType,attr"`
28 | ResultMap string `xml:"resultMap,attr"`
29 | FlushCache string `xml:"flushCache,attr"`
30 | UseCache string `xml:"useCache,attr"`
31 | Timeout string `xml:"timeout,attr"`
32 | FetchSize string `xml:"fetchSize,attr"`
33 | StatementType string `xml:"statementType,attr"`
34 | ResultSetType string `xml:"resultSetType,attr"`
35 |
36 | //If []If `xml:"if"`
37 | //Include Include `xml:"include"`
38 | //Where Where `xml:"where"`
39 | //Data string `xml:",chardata"`
40 | Data string `xml:",innerxml"`
41 | }
42 |
43 | type Insert struct {
44 | XMLName xml.Name
45 | Id string `xml:"id,attr"`
46 | ParameterType string `xml:"parameterType,attr"`
47 | FlushCache string `xml:"flushCache,attr"`
48 | Timeout string `xml:"timeout,attr"`
49 | StatementType string `xml:"statementType,attr"`
50 | UseGeneratedKeys string `xml:"useGeneratedKeys,attr"`
51 | KeyProperty string `xml:"keyProperty,attr"`
52 | KeyColumn string `xml:"keyColumn,attr"`
53 |
54 | //If []If `xml:"if"`
55 | //Include Include `xml:"include"`
56 | //Where Where `xml:"where"`
57 | //Data string `xml:",chardata"`
58 | Data string `xml:",innerxml"`
59 | }
60 |
61 | type Update struct {
62 | XMLName xml.Name
63 | Id string `xml:"id,attr"`
64 | ParameterType string `xml:"parameterType,attr"`
65 | FlushCache string `xml:"flushCache,attr"`
66 | Timeout string `xml:"timeout,attr"`
67 | StatementType string `xml:"statementType,attr"`
68 |
69 | //If []If `xml:"if"`
70 | //Include Include `xml:"include"`
71 | //Set Set `xml:"set"`
72 | //Where Where `xml:"where"`
73 | //Data string `xml:",chardata"`
74 | Data string `xml:",innerxml"`
75 | }
76 |
77 | type Delete struct {
78 | XMLName xml.Name
79 | Id string `xml:"id,attr"`
80 | ParameterType string `xml:"parameterType,attr"`
81 | FlushCache string `xml:"flushCache,attr"`
82 | Timeout string `xml:"timeout,attr"`
83 | StatementType string `xml:"statementType,attr"`
84 |
85 | //If []If `xml:"if"`
86 | //Include Include `xml:"include"`
87 | //Where Where `xml:"where"`
88 | //Data string `xml:",chardata"`
89 | Data string `xml:",innerxml"`
90 | }
91 |
92 | func (a *Select) ParseDynamic() {
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/parsing/xml/manager.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package xml
19 |
20 | import (
21 | "sync"
22 |
23 | "github.com/acmestack/gobatis/errors"
24 | "github.com/acmestack/gobatis/logging"
25 | "github.com/acmestack/gobatis/parsing"
26 | "github.com/acmestack/gobatis/parsing/sqlparser"
27 | )
28 |
29 | type Manager struct {
30 | sqlMap map[string]*parsing.DynamicData
31 | lock sync.Mutex
32 | }
33 |
34 | func NewManager() *Manager {
35 | return &Manager{
36 | sqlMap: map[string]*parsing.DynamicData{},
37 | }
38 | }
39 |
40 | func (manager *Manager) RegisterData(data []byte) error {
41 | manager.lock.Lock()
42 | defer manager.lock.Unlock()
43 |
44 | mapper, err := Parse(data)
45 | if err != nil {
46 | logging.Warn("register mapper data failed: %s err: %v\n", string(data), err)
47 | return err
48 | }
49 |
50 | return manager.formatMapper(mapper)
51 | }
52 |
53 | func (manager *Manager) RegisterFile(file string) error {
54 | manager.lock.Lock()
55 | defer manager.lock.Unlock()
56 |
57 | mapper, err := ParseFile(file)
58 | if err != nil {
59 | logging.Warn("register mapper file failed: %s err: %v\n", file, err)
60 | return err
61 | }
62 |
63 | return manager.formatMapper(mapper)
64 | }
65 |
66 | func (manager *Manager) formatMapper(mapper *Mapper) error {
67 | ret := mapper.Format()
68 | for k, v := range ret {
69 | if _, ok := manager.sqlMap[k]; ok {
70 | return errors.SqlIdDuplicates
71 | } else {
72 | manager.sqlMap[k] = v
73 | }
74 | }
75 | return nil
76 | }
77 |
78 | func (manager *Manager) FindSqlParser(sqlId string) (sqlparser.SqlParser, bool) {
79 | manager.lock.Lock()
80 | defer manager.lock.Unlock()
81 |
82 | v, ok := manager.sqlMap[sqlId]
83 | return v, ok
84 | }
85 |
86 | func (manager *Manager) RegisterSql(sqlId string, sql string) error {
87 | manager.lock.Lock()
88 | defer manager.lock.Unlock()
89 |
90 | if _, ok := manager.sqlMap[sqlId]; ok {
91 | return errors.SqlIdDuplicates
92 | } else {
93 | dd := &parsing.DynamicData{OriginData: sql}
94 | manager.sqlMap[sqlId] = dd
95 | }
96 | return nil
97 | }
98 |
99 | func (manager *Manager) UnregisterSql(sqlId string) {
100 | manager.lock.Lock()
101 | defer manager.lock.Unlock()
102 |
103 | delete(manager.sqlMap, sqlId)
104 | }
105 |
--------------------------------------------------------------------------------
/parsing/xml/parse.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package xml
19 |
20 | import (
21 | "encoding/xml"
22 | "io"
23 | "io/ioutil"
24 | "os"
25 |
26 | "github.com/acmestack/gobatis/logging"
27 | )
28 |
29 | const (
30 | MapperStart = "mapper"
31 | )
32 |
33 | func ParseFile(path string) (*Mapper, error) {
34 | file, err := os.Open(path) // For read access.
35 | if err != nil {
36 | logging.Warn("error: %v", err)
37 | return nil, err
38 | }
39 | defer file.Close()
40 | data, err := ioutil.ReadAll(file)
41 | if err != nil {
42 | logging.Warn("error: %v", err)
43 | return nil, err
44 | }
45 |
46 | return Parse(data)
47 | }
48 |
49 | func Parse(data []byte) (*Mapper, error) {
50 | v := Mapper{}
51 | err := xml.Unmarshal(data, &v)
52 | if err != nil {
53 | logging.Warn("error: %v", err)
54 | return nil, err
55 | }
56 | return &v, nil
57 | }
58 |
59 | func parseInner(reader io.Reader) {
60 | decoder := xml.NewDecoder(reader)
61 | var strName string
62 | for {
63 | token, err := decoder.Token()
64 | if err != nil {
65 | break
66 | }
67 |
68 | name := getStartElementName(token)
69 | if name != nil {
70 | if name.Local == MapperStart {
71 | switch t := token.(type) {
72 | case xml.StartElement:
73 | startElement := xml.StartElement(t)
74 | logging.Debug("start: ", startElement.Name.Local)
75 | strName = startElement.Name.Local
76 | case xml.EndElement:
77 | endElement := xml.EndElement(t)
78 | logging.Debug("end: ", endElement.Name.Local)
79 | case xml.CharData:
80 | data := xml.CharData(t)
81 | str := string(data)
82 | switch strName {
83 | case "City":
84 | logging.Debug("city:", str)
85 | case "first":
86 | logging.Debug("first: ", str)
87 | }
88 | }
89 | break
90 | }
91 | }
92 | }
93 | }
94 |
95 | func getStartElementName(token xml.Token) *xml.Name {
96 | switch t := token.(type) {
97 | case xml.StartElement:
98 | startElement := xml.StartElement(t)
99 | logging.Debug("start: ", startElement.Name.Local)
100 | return &startElement.Name
101 | }
102 | return nil
103 | }
104 |
105 | // * If the struct has a field of type []byte or string with tag
106 | // ",innerxml", Unmarshal accumulates the raw XML nested inside the
107 | // element in that field. The rest of the rules still apply.
108 | //
109 | // * If the struct has a field named XMLName of type Name,
110 | // Unmarshal records the element name in that field.
111 | //
112 | // * If the XMLName field has an associated tag of the form
113 | // "name" or "namespace-URL name", the XML element must have
114 | // the given name (and, optionally, name space) or else Unmarshal
115 | // returns an error.
116 | //
117 | // * If the XML element has an attribute whose name matches a
118 | // struct field name with an associated tag containing ",attr" or
119 | // the explicit name in a struct field tag of the form "name,attr",
120 | // Unmarshal records the attribute value in that field.
121 | //
122 | // * If the XML element has an attribute not handled by the previous
123 | // rule and the struct has a field with an associated tag containing
124 | // ",any,attr", Unmarshal records the attribute value in the first
125 | // such field.
126 | //
127 | // * If the XML element contains character data, that data is
128 | // accumulated in the first struct field that has tag ",chardata".
129 | // The struct field may have type []byte or string.
130 | // If there is no such field, the character data is discarded.
131 | //
132 | // * If the XML element contains comments, they are accumulated in
133 | // the first struct field that has tag ",comment". The struct
134 | // field may have type []byte or string. If there is no such
135 | // field, the comments are discarded.
136 | //
137 | // * If the XML element contains a sub-element whose name matches
138 | // the prefix of a tag formatted as "a" or "a>b>c", unmarshal
139 | // will descend into the XML structure looking for elements with the
140 | // given names, and will map the innermost elements to that struct
141 | // field. A tag starting with ">" is equivalent to one starting
142 | // with the field name followed by ">".
143 | //
144 | // * If the XML element contains a sub-element whose name matches
145 | // a struct field's XMLName tag and the struct field has no
146 | // explicit name tag as per the previous rule, unmarshal maps
147 | // the sub-element to that struct field.
148 | //
149 | // * If the XML element contains a sub-element whose name matches a
150 | // field without any mode flags (",attr", ",chardata", etc), Unmarshal
151 | // maps the sub-element to that struct field.
152 | //
153 | // * If the XML element contains a sub-element that hasn't matched any
154 | // of the above rules and the struct has a field with tag ",any",
155 | // unmarshal maps the sub-element to that struct field.
156 | //
157 | // * An anonymous struct field is handled as if the fields of its
158 | // value were part of the outer struct.
159 | //
160 | // * A struct field with tag "-" is never unmarshaled into.
161 | //
162 |
--------------------------------------------------------------------------------
/parsing/xml/resultmap.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package xml
19 |
20 | import "encoding/xml"
21 |
22 | type IdArg struct {
23 | Column string `xml:"column,attr"`
24 | GoType string `xml:"type,attr"`
25 | }
26 |
27 | type Constructor struct {
28 | IdArg IdArg `xml:"idArg"`
29 | Arg string `xml:"arg"`
30 | }
31 |
32 | type Result struct {
33 | Property string `xml:"property,attr"`
34 | Column string `xml:"column,attr"`
35 | }
36 |
37 | type ResultMap struct {
38 | XMLName xml.Name
39 | //id
40 | Id string `xml:"id,attr"`
41 | //struct类型名称
42 | TypeName string `xml:"type,attr"`
43 | //constructor - 用于在实例化类时,注入结果到构造方法中
44 | Constructor Constructor `xml:"constructor"`
45 | //一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
46 | ResultId Result `xml:"id"`
47 | //注入到字段或 Struct 属性的普通结果
48 | Results []Result `xml:"result"`
49 | //TODO:
50 | //association: 一个复杂类型的关联;许多结果将包装成这种类型
51 | //collection: 一个复杂类型的集合
52 | //discriminator: 使用结果值来决定使用哪个 resultMap
53 | }
54 |
--------------------------------------------------------------------------------
/parsing/xml/root.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package xml
19 |
20 | import (
21 | "strings"
22 |
23 | "github.com/acmestack/gobatis/logging"
24 | "github.com/acmestack/gobatis/parsing"
25 | )
26 |
27 | type Mapper struct {
28 | Namespace string `xml:"namespace,attr"`
29 | ResultMaps []ResultMap `xml:"resultMap"`
30 | Sql []Sql `xml:"sql"`
31 |
32 | Insert []Insert `xml:"insert"`
33 | Update []Update `xml:"update"`
34 | Select []Select `xml:"select"`
35 | Delete []Delete `xml:"delete"`
36 | }
37 |
38 | func (mapper *Mapper) Format() map[string]*parsing.DynamicData {
39 | ret := map[string]*parsing.DynamicData{}
40 | keyPre := strings.TrimSpace(mapper.Namespace)
41 | if keyPre != "" {
42 | keyPre = keyPre + "."
43 | }
44 | for _, v := range mapper.Insert {
45 | key := keyPre + v.Id
46 | if d, ok := ret[key]; ok {
47 | logging.Warn("Insert Sql id is duplicates, id: %s, before: %s, after %s\n", v.Id, d, v.Data)
48 | }
49 | d, err := ParseDynamic(strings.TrimSpace(v.Data), mapper.Sql)
50 | if err == nil {
51 | ret[key] = d
52 | }
53 | }
54 | for _, v := range mapper.Update {
55 | key := keyPre + v.Id
56 | if d, ok := ret[key]; ok {
57 | logging.Warn("Update Sql id is duplicates, id: %s, before: %s, after %s\n", v.Id, d, v.Data)
58 | }
59 | d, err := ParseDynamic(strings.TrimSpace(v.Data), mapper.Sql)
60 | if err == nil {
61 | ret[key] = d
62 | }
63 | }
64 | for _, v := range mapper.Select {
65 | key := keyPre + v.Id
66 | if d, ok := ret[key]; ok {
67 | logging.Warn("Select Sql id is duplicates, id: %s, before: %s, after %s\n", v.Id, d, v.Data)
68 | }
69 | d, err := ParseDynamic(strings.TrimSpace(v.Data), mapper.Sql)
70 | if err == nil {
71 | ret[key] = d
72 | }
73 | }
74 | for _, v := range mapper.Delete {
75 | key := keyPre + v.Id
76 | if d, ok := ret[v.Id]; ok {
77 | logging.Warn("Delete Sql id is duplicates, id: %s, before: %s, after %s\n", v.Id, d, v.Data)
78 | }
79 | d, err := ParseDynamic(strings.TrimSpace(v.Data), mapper.Sql)
80 | if err == nil {
81 | ret[key] = d
82 | }
83 | }
84 | return ret
85 | }
86 |
--------------------------------------------------------------------------------
/reflection/parseparam.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package reflection
19 |
20 | import (
21 | "fmt"
22 | "reflect"
23 | "strconv"
24 | "strings"
25 | )
26 |
27 | const (
28 | sliceParamSeparator = "_&eLEm_"
29 | )
30 |
31 | type paramParser struct {
32 | ret map[string]interface{}
33 | index int
34 | }
35 |
36 | func ParseParams(params ...interface{}) map[string]interface{} {
37 | parser := paramParser{
38 | ret: map[string]interface{}{},
39 | index: 0,
40 | }
41 | parser.innerParse(params...)
42 | return parser.ret
43 | }
44 |
45 | func (parser *paramParser) innerParse(params ...interface{}) {
46 | for i := range params {
47 | parser.parseOne("", params[i])
48 | }
49 | }
50 |
51 | func (parser *paramParser) parseOne(parentKey string, v interface{}) {
52 | rt := reflect.TypeOf(v)
53 | rv := reflect.ValueOf(v)
54 |
55 | if rt.Kind() == reflect.Ptr {
56 | rt = rt.Elem()
57 | rv = rv.Elem()
58 | }
59 |
60 | if IsSimpleType(rt) {
61 | if parentKey == "" {
62 | parser.ret[parentKey+strconv.Itoa(parser.index)] = v
63 | parser.index++
64 | } else {
65 | parser.ret[parentKey[:len(parentKey)-1]] = v
66 | }
67 | } else if rt.Kind() == reflect.Struct {
68 | oi, _ := GetStructInfo(v)
69 | structMap := oi.MapValue()
70 | for key, value := range structMap {
71 | parser.ret[parentKey+structKey(oi, key)] = value
72 | }
73 | } else if rt.Kind() == reflect.Slice {
74 | l := rv.Len()
75 | for i := 0; i < l; i++ {
76 | elemV := rv.Index(i)
77 | if !elemV.CanInterface() {
78 | elemV = reflect.Indirect(elemV)
79 | }
80 | parser.parseOne(fmt.Sprintf("%s%d[%d].", parentKey, parser.index, i), elemV.Interface())
81 | }
82 | parser.ret[strconv.Itoa(parser.index)] = l
83 | parser.index++
84 | //l := rv.Len()
85 | //builder := strings.Builder{}
86 | //for i := 0; i < l; i++ {
87 | // elemV := rv.Index(i)
88 | // if !elemV.CanInterface() {
89 | // elemV = reflect.Indirect(elemV)
90 | // }
91 | // if elemV.Kind() == reflect.String {
92 | // builder.WriteString(elemV.String())
93 | // } else {
94 | // var str string
95 | // if SafeSetValue(reflect.ValueOf(&str), elemV.Interface()) {
96 | // builder.WriteString(str)
97 | // } else {
98 | // //log
99 | // }
100 | // }
101 | //
102 | // if i < l-1 {
103 | // builder.WriteString(slice_param_separator)
104 | // }
105 | //}
106 | //parser.ret[strconv.Itoa(parser.index)] = builder.String()
107 | //parser.index++
108 | } else if rt.Kind() == reflect.Map {
109 | keys := rv.MapKeys()
110 | for _, key := range keys {
111 | if key.Kind() == reflect.String {
112 | value := rv.MapIndex(key)
113 | value = value.Elem()
114 | if IsSimpleType(value.Type()) {
115 | if !value.CanInterface() {
116 | value = reflect.Indirect(value)
117 | }
118 | parser.ret[parentKey+key.String()] = value
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | func ParseSliceParamString(src string) []string {
126 | return strings.Split(src, sliceParamSeparator)
127 | }
128 |
129 | func (parser *paramParser) setSliceValue(parentKey string) string {
130 | key := fmt.Sprintf("%s%d[", parentKey, parser.index)
131 | builder := strings.Builder{}
132 | parser.ret[strconv.Itoa(parser.index)] = builder.String()
133 | for k := range parser.ret {
134 | if strings.Index(k, key) == 0 {
135 | builder.WriteString(k)
136 | builder.WriteString(sliceParamSeparator)
137 | }
138 | }
139 |
140 | s := builder.String()
141 | if len(s) > 7 {
142 | return s[:len(s)-7]
143 | } else {
144 | return s
145 | }
146 | }
147 |
148 | func structKey(oi *StructInfo, field string) string {
149 | return oi.Name + "." + field
150 | }
151 |
--------------------------------------------------------------------------------
/reflection/type.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package reflection
19 |
20 | import (
21 | "reflect"
22 | "time"
23 | )
24 |
25 | var (
26 | EmptyString string
27 | BoolDefault bool
28 | ByteDefault byte
29 | Complex64Default complex64
30 | Complex128Default complex128
31 | Float32Default float32
32 | Float64Default float64
33 | Int64Default int64
34 | Uint64Default uint64
35 | Int32Default int32
36 | Uint32Default uint32
37 | Int16Default int16
38 | Uint16Default uint16
39 | Int8Default int8
40 | Uint8Default uint8
41 | IntDefault int
42 | UintDefault uint
43 | TimeDefault time.Time
44 | )
45 |
46 | var (
47 | IntType = reflect.TypeOf(IntDefault)
48 | Int8Type = reflect.TypeOf(Int8Default)
49 | Int16Type = reflect.TypeOf(Int16Default)
50 | Int32Type = reflect.TypeOf(Int32Default)
51 | Int64Type = reflect.TypeOf(Int64Default)
52 |
53 | UintType = reflect.TypeOf(UintDefault)
54 | Uint8Type = reflect.TypeOf(Uint8Default)
55 | Uint16Type = reflect.TypeOf(Uint16Default)
56 | Uint32Type = reflect.TypeOf(Uint32Default)
57 | Uint64Type = reflect.TypeOf(Uint64Default)
58 |
59 | Float32Type = reflect.TypeOf(Float32Default)
60 | Float64Type = reflect.TypeOf(Float64Default)
61 |
62 | Complex64Type = reflect.TypeOf(Complex64Default)
63 | Complex128Type = reflect.TypeOf(Complex128Default)
64 |
65 | StringType = reflect.TypeOf(EmptyString)
66 | BoolType = reflect.TypeOf(BoolDefault)
67 | ByteType = reflect.TypeOf(ByteDefault)
68 | BytesType = reflect.SliceOf(ByteType)
69 |
70 | TimeType = reflect.TypeOf(TimeDefault)
71 | )
72 |
73 | var (
74 | IntKind = IntType.Kind()
75 | Int8Kind = Int8Type.Kind()
76 | Int16Kind = Int16Type.Kind()
77 | Int32Kind = Int32Type.Kind()
78 | Int64Kind = Int64Type.Kind()
79 |
80 | UintKind = UintType.Kind()
81 | Uint8Kind = Uint8Type.Kind()
82 | Uint16Kind = Uint16Type.Kind()
83 | Uint32Kind = Uint32Type.Kind()
84 | Uint64Kind = Uint64Type.Kind()
85 |
86 | Float32Kind = Float32Type.Kind()
87 | Float64Kind = Float64Type.Kind()
88 |
89 | Complex64Kind = Complex64Type.Kind()
90 | Complex128Kind = Complex128Type.Kind()
91 |
92 | StringKind = StringType.Kind()
93 | BoolKind = BoolType.Kind()
94 | ByteKind = ByteType.Kind()
95 | BytesKind = BytesType.Kind()
96 |
97 | TimeKind = TimeType.Kind()
98 | )
99 |
100 | var SqlType2GoType = map[string]reflect.Type{
101 | "int": IntType,
102 | "integer": IntType,
103 | "tinyint": IntType,
104 | "smallint": IntType,
105 | "mediumint": IntType,
106 | "bigint": Int64Type,
107 | "int unsigned": UintType,
108 | "integer unsigned": UintType,
109 | "tinyint unsigned": UintType,
110 | "smallint unsigned": UintType,
111 | "mediumint unsigned": UintType,
112 | "bigint unsigned": Uint64Type,
113 | "bit": Int8Type,
114 | "bool": BoolType,
115 | "enum": StringType,
116 | "set": StringType,
117 | "varchar": StringType,
118 | "char": StringType,
119 | "tinytext": StringType,
120 | "mediumtext": StringType,
121 | "text": StringType,
122 | "longtext": StringType,
123 | "blob": StringType,
124 | "tinyblob": StringType,
125 | "mediumblob": StringType,
126 | "longblob": StringType,
127 | "date": TimeType,
128 | "datetime": TimeType,
129 | "timestamp": TimeType,
130 | "time": TimeType,
131 | "float": Float64Type,
132 | "double": Float64Type,
133 | "decimal": Float64Type,
134 | "binary": StringType,
135 | "varbinary": StringType,
136 | }
137 |
--------------------------------------------------------------------------------
/session/default_session.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package session
19 |
20 | import (
21 | "context"
22 | "fmt"
23 |
24 | "github.com/acmestack/gobatis/common"
25 | "github.com/acmestack/gobatis/executor"
26 | "github.com/acmestack/gobatis/logging"
27 | "github.com/acmestack/gobatis/reflection"
28 | "github.com/acmestack/gobatis/transaction"
29 | )
30 |
31 | type DefaultSqlSession struct {
32 | Log logging.LogFunc
33 | tx transaction.Transaction
34 | executor executor.Executor
35 | autoCommit bool
36 | }
37 |
38 | func NewDefaultSqlSession(log logging.LogFunc, tx transaction.Transaction, e executor.Executor, autoCommit bool) *DefaultSqlSession {
39 | ret := &DefaultSqlSession{
40 | Log: log,
41 | tx: tx,
42 | executor: e,
43 | autoCommit: autoCommit,
44 | }
45 | return ret
46 | }
47 |
48 | func (session *DefaultSqlSession) Close(rollback bool) {
49 | session.executor.Close(rollback)
50 | }
51 |
52 | func (session *DefaultSqlSession) Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error {
53 | session.logLastSql(sql, params...)
54 | return session.executor.Query(ctx, result, sql, params...)
55 | }
56 |
57 | func (session *DefaultSqlSession) Insert(ctx context.Context, sql string, params ...interface{}) (int64, int64, error) {
58 | session.logLastSql(sql, params...)
59 | ret, err := session.exec(ctx, sql, params...)
60 | if err != nil {
61 | return 0, -1, err
62 | }
63 |
64 | count, err := ret.RowsAffected()
65 | if err != nil {
66 | return 0, -1, err
67 | }
68 |
69 | id, err := ret.LastInsertId()
70 | if err != nil {
71 | return count, id, err
72 | }
73 | return count, id, nil
74 | }
75 |
76 | func (session *DefaultSqlSession) Update(ctx context.Context, sql string, params ...interface{}) (int64, error) {
77 | session.logLastSql(sql, params...)
78 | ret, err := session.exec(ctx, sql, params...)
79 | if err != nil {
80 | return 0, err
81 | }
82 |
83 | count, err := ret.RowsAffected()
84 | if err != nil {
85 | return 0, err
86 | }
87 | return count, nil
88 | }
89 |
90 | func (session *DefaultSqlSession) Delete(ctx context.Context, sql string, params ...interface{}) (int64, error) {
91 | session.logLastSql(sql, params...)
92 | ret, err := session.exec(ctx, sql, params...)
93 | if err != nil {
94 | return 0, err
95 | }
96 |
97 | count, err := ret.RowsAffected()
98 | if err != nil {
99 | return 0, err
100 | }
101 | return count, nil
102 | }
103 |
104 | func (session *DefaultSqlSession) Begin() error {
105 | session.logLastSql("Begin", "")
106 | return session.tx.Begin()
107 | }
108 |
109 | func (session *DefaultSqlSession) Commit() error {
110 | session.logLastSql("Commit", "")
111 | return session.tx.Commit()
112 | }
113 |
114 | func (session *DefaultSqlSession) Rollback() error {
115 | session.logLastSql("Rollback", "")
116 | return session.tx.Rollback()
117 | }
118 |
119 | func (session *DefaultSqlSession) logLastSql(sql string, params ...interface{}) {
120 | session.Log(logging.INFO, "sql: [%s], param: %s\n", sql, fmt.Sprint(params...))
121 | }
122 |
123 | func (session *DefaultSqlSession) exec(ctx context.Context, sql string, params ...interface{}) (common.Result, error) {
124 | return session.executor.Exec(ctx, sql, params...)
125 | }
126 |
--------------------------------------------------------------------------------
/session/propagation.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package session
19 |
20 | //
21 | //import "github.com/acmestack/gobatis/errors"
22 | //
23 | //type Propagation interface {
24 | // Begin() error
25 | // Commit() error
26 | // Rollback() error
27 | //}
28 | //
29 | //type PropagationBase struct {
30 | // sess SqlSession
31 | //}
32 | //
33 | //func (p *PropagationBase) SetSession(sess SqlSession) {
34 | // p.sess = sess
35 | //}
36 | //
37 | ////当前没有事务则新建事务,有则加入当前事务
38 | //type PropagationRequired struct {
39 | // PropagationBase
40 | //
41 | // beginCount int32
42 | //}
43 | //
44 | //func (p *PropagationRequired) Begin() error {
45 | // if p.beginCount > 0 {
46 | // return nil
47 | // }
48 | //
49 | // err := p.sess.Begin()
50 | // if err != nil {
51 | // return err
52 | // }
53 | //
54 | // p.beginCount++
55 | // return nil
56 | //}
57 | //
58 | //func (p *PropagationRequired) Commit() error {
59 | // if p.beginCount == 0 {
60 | // return errors.TRANSACTION_WITHOUT_BEGIN
61 | // }
62 | //
63 | // p.beginCount--
64 | // if p.beginCount == 0 {
65 | // return p.sess.Commit()
66 | // }
67 | // return nil
68 | //}
69 | //
70 | //func (p *PropagationRequired) Rollback() error {
71 | // if p.beginCount == 0 {
72 | // return errors.TRANSACTION_WITHOUT_BEGIN
73 | // }
74 | //
75 | // p.beginCount--
76 | // if p.beginCount == 0 {
77 | // return p.sess.Rollback()
78 | // }
79 | // return nil
80 | //}
81 | //
82 | ////支持当前事务,如果当前没有事务则以非事务方式执行
83 | //type PropagationSupports struct {
84 | // PropagationBase
85 | //}
86 | //
87 | //func (p *PropagationSupports) Begin() error {
88 | //
89 | //}
90 | //
91 | //func (p *PropagationSupports) Commit() error {
92 | // if p.beginCount == 0 {
93 | // return errors.TRANSACTION_WITHOUT_BEGIN
94 | // }
95 | //
96 | // p.beginCount--
97 | // if p.beginCount == 0 {
98 | // return p.sess.Commit()
99 | // }
100 | // return nil
101 | //}
102 | //
103 | //func (p *PropagationSupports) Rollback() error {
104 | // if p.beginCount == 0 {
105 | // return errors.TRANSACTION_WITHOUT_BEGIN
106 | // }
107 | //
108 | // p.beginCount--
109 | // if p.beginCount == 0 {
110 | // return p.sess.Rollback()
111 | // }
112 | // return nil
113 | //}
114 | //
115 | ////使用当前事务,如果没有则panic
116 | //type PropagationMandatory struct {
117 | // PropagationBase
118 | //}
119 | //
120 | ////新建事务,如果当前有事务则把当前事务挂起
121 | //type PropagationRequiredNew struct {
122 | // PropagationBase
123 | //}
124 | //
125 | ////以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
126 | //type PropagationNotSupported struct {
127 | // PropagationBase
128 | //}
129 | //
130 | ////以非事务的方式执行,如果当前有事务则panic
131 | //type PropagationNever struct {
132 | // PropagationBase
133 | //}
134 | //
135 | ////如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
136 | //type PropagationNested struct {
137 | // PropagationBase
138 | //}
139 |
--------------------------------------------------------------------------------
/session/sqlsession.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package session
19 |
20 | import (
21 | "context"
22 | "github.com/acmestack/gobatis/reflection"
23 | )
24 |
25 | type SqlSession interface {
26 | Close(rollback bool)
27 |
28 | Query(ctx context.Context, result reflection.Object, sql string, params ...interface{}) error
29 |
30 | Insert(ctx context.Context, sql string, params ...interface{}) (int64, int64, error)
31 |
32 | Update(ctx context.Context, sql string, params ...interface{}) (int64, error)
33 |
34 | Delete(ctx context.Context, sql string, params ...interface{}) (int64, error)
35 |
36 | Begin() error
37 |
38 | Commit() error
39 |
40 | Rollback() error
41 | }
42 |
--------------------------------------------------------------------------------
/sqlmanager.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package gobatis
19 |
20 | import (
21 | "os"
22 | "path/filepath"
23 |
24 | "github.com/acmestack/gobatis/parsing"
25 | "github.com/acmestack/gobatis/parsing/sqlparser"
26 | "github.com/acmestack/gobatis/parsing/template"
27 | "github.com/acmestack/gobatis/parsing/xml"
28 | )
29 |
30 | type sqlManager struct {
31 | dynamicSqlMgr *xml.Manager
32 | templateSqlMgr *template.Manager
33 | }
34 |
35 | var sqlMgr = sqlManager{
36 | dynamicSqlMgr: xml.NewManager(),
37 | templateSqlMgr: template.NewManager(),
38 | }
39 |
40 | func RegisterSql(sqlId string, sql string) error {
41 | return sqlMgr.dynamicSqlMgr.RegisterSql(sqlId, sql)
42 | }
43 |
44 | func UnregisterSql(sqlId string) {
45 | sqlMgr.dynamicSqlMgr.UnregisterSql(sqlId)
46 | }
47 |
48 | func RegisterMapperData(data []byte) error {
49 | return sqlMgr.dynamicSqlMgr.RegisterData(data)
50 | }
51 |
52 | func RegisterMapperFile(file string) error {
53 | return sqlMgr.dynamicSqlMgr.RegisterFile(file)
54 | }
55 |
56 | func FindDynamicSqlParser(sqlId string) (sqlparser.SqlParser, bool) {
57 | return sqlMgr.dynamicSqlMgr.FindSqlParser(sqlId)
58 | }
59 |
60 | func RegisterTemplateData(data []byte) error {
61 | return sqlMgr.templateSqlMgr.RegisterData(data)
62 | }
63 |
64 | func RegisterTemplateFile(file string) error {
65 | return sqlMgr.templateSqlMgr.RegisterFile(file)
66 | }
67 |
68 | func FindTemplateSqlParser(sqlId string) (sqlparser.SqlParser, bool) {
69 | return sqlMgr.templateSqlMgr.FindSqlParser(sqlId)
70 | }
71 |
72 | type ParserFactory func(sql string) (sqlparser.SqlParser, error)
73 |
74 | func DynamicParserFactory(sql string) (sqlparser.SqlParser, error) {
75 | return &parsing.DynamicData{OriginData: sql}, nil
76 | }
77 |
78 | func TemplateParserFactory(sql string) (sqlparser.SqlParser, error) {
79 | return template.CreateParser([]byte(sql))
80 | }
81 |
82 | func ScanMapperFile(dir string) error {
83 | return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
84 | if err != nil {
85 | return err
86 | }
87 | if !info.IsDir() {
88 | filename := filepath.Base(path)
89 | length := len(filename)
90 | if length > 4 {
91 | if filename[length-4:] == ".xml" {
92 | err := RegisterMapperFile(path)
93 | if err != nil {
94 | return err
95 | }
96 | }
97 | if filename[length-4:] == ".tpl" {
98 | err := RegisterTemplateFile(path)
99 | if err != nil {
100 | return err
101 | }
102 | }
103 | }
104 | }
105 | return nil
106 | })
107 | }
108 |
--------------------------------------------------------------------------------
/statement/statement.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package statement
19 |
20 | import (
21 | "context"
22 | "github.com/acmestack/gobatis/common"
23 | "github.com/acmestack/gobatis/reflection"
24 | )
25 |
26 | type Statement interface {
27 | Query(ctx context.Context, result reflection.Object, params ...interface{}) error
28 | Exec(ctx context.Context, params ...interface{}) (common.Result, error)
29 | Close()
30 | }
31 |
--------------------------------------------------------------------------------
/test/cmd/pg_cmd_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "errors"
22 | "testing"
23 |
24 | "github.com/acmestack/gobatis"
25 | "github.com/acmestack/gobatis/datasource"
26 | _ "github.com/lib/pq"
27 | )
28 |
29 | var sessionMgr *gobatis.SessionManager
30 |
31 | func init() {
32 | fac := gobatis.NewFactory(
33 | gobatis.SetMaxConn(100),
34 | gobatis.SetMaxIdleConn(50),
35 | gobatis.SetDataSource(&datasource.PostgreDataSource{
36 | Host: "localhost",
37 | Port: 5432,
38 | DBName: "testdb",
39 | Username: "test",
40 | Password: "test",
41 | }))
42 | sessionMgr = gobatis.NewSessionManager(fac)
43 | }
44 |
45 | func TestSelect(t *testing.T) {
46 | ret, err := SelectTestTable(sessionMgr.NewSession(), TestTable{Username: "test_user"})
47 |
48 | if err != nil {
49 | t.Fatal(err)
50 | }
51 |
52 | for _, v := range ret {
53 | t.Logf("value: %v", v)
54 | }
55 | }
56 |
57 | func TestSelectCount(t *testing.T) {
58 | //all count
59 | ret, err := SelectTestTableCount(sessionMgr.NewSession(), TestTable{})
60 | if err != nil {
61 | t.Fatal(err)
62 | }
63 | t.Logf("count is %d", ret)
64 | }
65 |
66 | func TestInsert(t *testing.T) {
67 | ret, id, err := InsertTestTable(sessionMgr.NewSession(), TestTable{Username: "test_insert_user", Password: "test_pw"})
68 | if err != nil {
69 | t.Log(err)
70 | }
71 | t.Logf("insert ret is %d, id is %d", ret, id)
72 | }
73 |
74 | func TestInsertBatch(t *testing.T) {
75 | ret, id, err := InsertBatchTestTable(sessionMgr.NewSession(), []TestTable{
76 | {Username: "test_insert_user4", Password: "test_pw4"},
77 | {Username: "test_insert_user5", Password: "test_pw5"},
78 | })
79 | if err != nil {
80 | t.Log(err)
81 | }
82 | t.Logf("insert ret is %d, id is %d", ret, id)
83 | }
84 |
85 | func TestUpdate(t *testing.T) {
86 | ret, err := UpdateTestTable(sessionMgr.NewSession(), TestTable{Id: 1, Username: "test_insert_user", Password: "update_pw"})
87 | if err != nil {
88 | t.Fatal(err)
89 | }
90 | t.Logf("update ret is %d", ret)
91 | }
92 |
93 | func TestDelete(t *testing.T) {
94 | //Same as: DELETE FROM test_table WHERE username = 'test_insert_user'
95 | ret, err := DeleteTestTable(sessionMgr.NewSession(), TestTable{Username: "test_insert_user"})
96 | if err != nil {
97 | t.Fatal(err)
98 | }
99 | t.Logf("delete ret is %d", ret)
100 | }
101 |
102 | func TestSessionTpl(t *testing.T) {
103 | gobatis.RegisterTemplateFile("./template/test_table_mapper.tmpl")
104 | var param = TestTable{Id: 1, Username: "user", Password: "pw"}
105 | t.Run("select", func(t *testing.T) {
106 | sess := sessionMgr.NewSession()
107 | ret, _ := SelectTestTable(sess, TestTable{Id: 1})
108 | t.Log(ret)
109 | })
110 |
111 | t.Run("count", func(t *testing.T) {
112 | sess := sessionMgr.NewSession()
113 | ret, _ := SelectTestTableCount(sess, TestTable{Id: 1})
114 | t.Log(ret)
115 | })
116 |
117 | t.Run("insert", func(t *testing.T) {
118 | sess := sessionMgr.NewSession()
119 | ret, _, err := InsertTestTable(sess, param)
120 | t.Log(err)
121 | t.Log(ret)
122 | })
123 |
124 | t.Run("insertBatch", func(t *testing.T) {
125 | sess := sessionMgr.NewSession()
126 | ret, _, err := InsertBatchTestTable(sess, []TestTable{
127 | {Username: "test_insert_user14", Password: "test_pw14"},
128 | {Username: "test_insert_user15", Password: "test_pw15"},
129 | })
130 | t.Log(err)
131 | t.Log(ret)
132 | })
133 |
134 | t.Run("update", func(t *testing.T) {
135 | sess := sessionMgr.NewSession()
136 | ret, err := UpdateTestTable(sess, TestTable{Id: 1, Username: "user2", Password: "pw2"})
137 | t.Log(err)
138 | t.Log(ret)
139 | })
140 |
141 | t.Run("delete", func(t *testing.T) {
142 | sess := sessionMgr.NewSession()
143 | ret, err := DeleteTestTable(sess, TestTable{Id: 1})
144 | t.Log(ret)
145 | t.Log(err)
146 | })
147 | }
148 |
149 | func TestSessionXml(t *testing.T) {
150 | gobatis.RegisterMapperFile("./xml/test_table_mapper.xml")
151 | var param = TestTable{Id: 1, Username: "user", Password: "pw"}
152 | t.Run("select", func(t *testing.T) {
153 | sess := sessionMgr.NewSession()
154 | ret, _ := SelectTestTable(sess, TestTable{Id: 1})
155 | t.Log(ret)
156 | })
157 |
158 | t.Run("count", func(t *testing.T) {
159 | sess := sessionMgr.NewSession()
160 | ret, _ := SelectTestTableCount(sess, TestTable{Id: 1})
161 | t.Log(ret)
162 | })
163 |
164 | t.Run("insert", func(t *testing.T) {
165 | sess := sessionMgr.NewSession()
166 | ret, _, err := InsertTestTable(sess, param)
167 | t.Log(err)
168 | t.Log(ret)
169 | })
170 |
171 | t.Run("insertBatch", func(t *testing.T) {
172 | sess := sessionMgr.NewSession()
173 | ret, _, err := InsertBatchTestTable(sess, []TestTable{
174 | {Username: "test_insert_user14", Password: "test_pw14"},
175 | {Username: "test_insert_user15", Password: "test_pw15"},
176 | })
177 | t.Log(err)
178 | t.Log(ret)
179 | })
180 |
181 | t.Run("update", func(t *testing.T) {
182 | sess := sessionMgr.NewSession()
183 | ret, err := UpdateTestTable(sess, TestTable{Id: 1, Username: "user2", Password: "pw2"})
184 | t.Log(err)
185 | t.Log(ret)
186 | })
187 |
188 | t.Run("delete", func(t *testing.T) {
189 | sess := sessionMgr.NewSession()
190 | ret, err := DeleteTestTable(sess, TestTable{Id: 1})
191 | t.Log(ret)
192 | t.Log(err)
193 | })
194 | }
195 |
196 | func TestTxSuccess(t *testing.T) {
197 | sessionMgr.NewSession().Tx(func(s *gobatis.Session) error {
198 | _, _, err := InsertTestTable(s, TestTable{Username: "tx_user", Password: "tx_pw"})
199 | t.Log(err)
200 | //select all
201 | ret, err := SelectTestTable(s, TestTable{})
202 | if err != nil {
203 | t.Fatal(err)
204 | }
205 | for _, v := range ret {
206 | t.Logf("value: %v", v)
207 | }
208 | //will commit
209 | return nil
210 | })
211 |
212 | //select all
213 | ret, _ := SelectTestTable(sessionMgr.NewSession(), TestTable{Username: "tx_user"})
214 |
215 | for _, v := range ret {
216 | t.Logf("value out tx: %v", v)
217 | }
218 | }
219 |
220 | func TestTxFail(t *testing.T) {
221 | sessionMgr.NewSession().Tx(func(s *gobatis.Session) error {
222 | _, _, err := InsertTestTable(s, TestTable{Username: "tx_user", Password: "tx_pw"})
223 | t.Log(err)
224 | ////select all
225 | //ret, err := SelectTestTable(s, TestTable{})
226 | //if err != nil {
227 | // t.Fatal(err)
228 | //}
229 | //for _, v := range ret {
230 | // t.Logf("value: %v", v)
231 | //}
232 | //will commit
233 | return errors.New("rollback")
234 | })
235 |
236 | //select all
237 | ret, _ := SelectTestTable(sessionMgr.NewSession(), TestTable{Username: "tx_user"})
238 |
239 | for _, v := range ret {
240 | t.Logf("value out tx: %v\n", v)
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/test/cmd/template/test_table_mapper.tmpl:
--------------------------------------------------------------------------------
1 | {{/*This file was generated by xfali/gobatis-cmd at*/}}
2 | {{/*2020-02-20 15:03:12.9953357 +0800 CST m=+0.061341301*/}}
3 |
4 | {{define "namespace"}}test{{end}}
5 |
6 | {{define "selectTestTable"}}
7 | SELECT id,username,password,createtime FROM test_table
8 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
9 | {{end}}
10 |
11 | {{define "selectTestTableCount"}}
12 | SELECT COUNT(*) FROM test_table
13 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
14 | {{end}}
15 |
16 | {{define "insertTestTable"}}
17 | INSERT INTO test_table(id,username,password,createtime)
18 | VALUES(
19 | {{arg .Id}}, {{arg .Username}}, {{arg .Password}}, {{arg .Createtime}})
20 | {{end}}
21 |
22 | {{define "insertBatchTestTable"}}
23 | {{$size := len . | add -1}}
24 | INSERT INTO test_table(id,username,password,createtime)
25 | VALUES {{range $i, $v := .}}
26 | ({{arg $v.Id}}, {{arg $v.Username}}, {{arg $v.Password}}, {{arg $v.Createtime}}){{if lt $i $size}},{{end}}
27 | {{end}}
28 | {{end}}
29 |
30 | {{define "updateTestTable"}}
31 | UPDATE test_table
32 | {{set .Id "id = " (arg .Id) "" | set .Username "username = " (arg .Username) | set .Password "password = " (arg .Password) | set .Createtime "createtime = " (arg .Createtime)}}
33 | {{where .Id "AND" "id = " (arg .Id) ""}}
34 | {{end}}
35 |
36 | {{define "deleteTestTable"}}
37 | DELETE FROM test_table
38 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
39 | {{end}}
40 |
41 |
--------------------------------------------------------------------------------
/test/cmd/test_table.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | "time"
23 | )
24 |
25 | type TestTable struct {
26 | //TableName gobatis.TableName `test_table`
27 | Id int `column:"id"`
28 | Username string `column:"username"`
29 | Password string `column:"password"`
30 | Createtime time.Time `column:"createtime"`
31 | }
32 |
33 | func (m *TestTable) Select(sess *gobatis.Session) ([]TestTable, error) {
34 | return SelectTestTable(sess, *m)
35 | }
36 |
37 | func (m *TestTable) Count(sess *gobatis.Session) (int64, error) {
38 | return SelectTestTableCount(sess, *m)
39 | }
40 |
41 | func (m *TestTable) Insert(sess *gobatis.Session) (int64, int64, error) {
42 | return InsertTestTable(sess, *m)
43 | }
44 |
45 | func (m *TestTable) Update(sess *gobatis.Session) (int64, error) {
46 | return UpdateTestTable(sess, *m)
47 | }
48 |
49 | func (m *TestTable) Delete(sess *gobatis.Session) (int64, error) {
50 | return DeleteTestTable(sess, *m)
51 | }
52 |
--------------------------------------------------------------------------------
/test/cmd/test_table_proxy.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | )
23 |
24 | func init() {
25 | modelV := TestTable{}
26 | gobatis.RegisterModel(&modelV)
27 | //gobatis.RegisterTemplateFile("./template/test_table_mapper.tmpl")
28 | }
29 |
30 | func SelectTestTable(sess *gobatis.Session, model TestTable) ([]TestTable, error) {
31 | var dataList []TestTable
32 | err := sess.Select("test.selectTestTable").Param(model).Result(&dataList)
33 | return dataList, err
34 | }
35 |
36 | func SelectTestTableCount(sess *gobatis.Session, model TestTable) (int64, error) {
37 | var ret int64
38 | err := sess.Select("test.selectTestTableCount").Param(model).Result(&ret)
39 | return ret, err
40 | }
41 |
42 | func InsertTestTable(sess *gobatis.Session, model TestTable) (int64, int64, error) {
43 | var ret int64
44 | runner := sess.Insert("test.insertTestTable").Param(model)
45 | err := runner.Result(&ret)
46 | id := runner.LastInsertId()
47 | return ret, id, err
48 | }
49 |
50 | func InsertBatchTestTable(sess *gobatis.Session, models []TestTable) (int64, int64, error) {
51 | var ret int64
52 | runner := sess.Insert("test.insertBatchTestTable").Param(models)
53 | err := runner.Result(&ret)
54 | id := runner.LastInsertId()
55 | return ret, id, err
56 | }
57 |
58 | func UpdateTestTable(sess *gobatis.Session, model TestTable) (int64, error) {
59 | var ret int64
60 | err := sess.Update("test.updateTestTable").Param(model).Result(&ret)
61 | return ret, err
62 | }
63 |
64 | func DeleteTestTable(sess *gobatis.Session, model TestTable) (int64, error) {
65 | var ret int64
66 | err := sess.Delete("test.deleteTestTable").Param(model).Result(&ret)
67 | return ret, err
68 | }
69 |
--------------------------------------------------------------------------------
/test/cmd/xml/test_table_mapper.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
22 | id,username,password,createtime
23 |
24 |
33 |
34 |
43 |
44 |
45 | INSERT INTO test_table (id,username,password,createtime)
46 | VALUES(
47 | #{TestTable.id},
48 | #{TestTable.username},
49 | #{TestTable.password},
50 | #{TestTable.createtime}
51 | )
52 |
53 |
54 |
55 | INSERT INTO test_table (id,username,password,createtime)
56 | VALUES
57 |
58 | (#{item.TestTable.id},#{item.TestTable.username},#{item.TestTable.password},#{item.TestTable.createtime})
59 |
60 |
61 |
62 |
63 | UPDATE test_table
64 |
65 | username = #{TestTable.username}
66 | password = #{TestTable.password}
67 | createtime = #{TestTable.createtime}
68 |
69 | WHERE id = #{TestTable.id}
70 |
71 |
72 |
73 | DELETE FROM test_table
74 |
75 | AND id = #{TestTable.id}
76 | AND username = #{TestTable.username}
77 | AND password = #{TestTable.password}
78 | AND createtime = #{TestTable.createtime}
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/test/object_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis/reflection"
22 | "reflect"
23 | "testing"
24 | "time"
25 | )
26 |
27 | func TestReflectObjectStruct(t *testing.T) {
28 | v := TestTable{}
29 | info, err := reflection.GetObjectInfo(&v)
30 | if err != nil {
31 | t.Fatal()
32 | }
33 | t.Logf("classname :%v", info.GetClassName())
34 | t.Log(v)
35 | newOne := TestTable{
36 | Username: "1",
37 | }
38 | info.SetValue(reflect.ValueOf(newOne))
39 |
40 | t.Logf("after set :%v\n", v)
41 |
42 | info.SetField("username", reflect.ValueOf("123"))
43 | t.Logf("after setField :%v\n", v)
44 | }
45 |
46 | func TestReflectObjectSimpleTime(t *testing.T) {
47 | v := time.Time{}
48 | info, err := reflection.GetObjectInfo(&v)
49 | if err != nil {
50 | t.Fatal()
51 | }
52 | t.Logf("classname :%v", info.GetClassName())
53 | t.Log(v)
54 | newOne := TestTable{
55 | Username: "1",
56 | }
57 | info.SetValue(reflect.ValueOf(newOne))
58 |
59 | t.Logf("after set error type :%v\n", v)
60 |
61 | info.SetValue(reflect.ValueOf(time.Now()))
62 |
63 | t.Logf("after set now type :%v\n", v)
64 |
65 | info.SetField("username", reflect.ValueOf("123"))
66 | t.Logf("after setField :%v\n", v)
67 | }
68 |
69 | func TestReflectObjectSimpleFloat(t *testing.T) {
70 | v := 0.0
71 | info, err := reflection.GetObjectInfo(&v)
72 | if err != nil {
73 | t.Fatal()
74 | }
75 | t.Logf("classname :%v", info.GetClassName())
76 | t.Log(v)
77 | info.SetValue(reflect.ValueOf(1))
78 |
79 | t.Logf("after set int type :%v\n", v)
80 |
81 | info.SetValue(reflect.ValueOf(1.5))
82 |
83 | t.Logf("after set float type :%v\n", v)
84 | }
85 |
86 | func TestReflectObjectMap(t *testing.T) {
87 | v := map[string]interface{}{}
88 | info, err := reflection.GetObjectInfo(&v)
89 | if err != nil {
90 | t.Fatal()
91 | }
92 | t.Logf("classname :%v", info.GetClassName())
93 | t.Log(v)
94 | info.SetValue(reflect.ValueOf(1))
95 |
96 | t.Logf("after set int type :%v\n", v)
97 |
98 | info.SetValue(reflect.ValueOf(map[string]int{"1": 1, "2": 2}))
99 |
100 | t.Logf("after set map[string]int type :%v\n", v)
101 |
102 | info.SetValue(reflect.ValueOf(map[string]interface{}{"1": 1, "2": 2}))
103 |
104 | t.Logf("after set map[string]interface{} type :%v\n", v)
105 |
106 | info.SetField("username", reflect.ValueOf("123"))
107 | t.Logf("after setField username 123 :%v\n", v)
108 |
109 | info.SetField("username", reflect.ValueOf("321"))
110 | t.Logf("after setField username 321 :%v\n", v)
111 | }
112 |
113 | func TestReflectObjectSlice2(t *testing.T) {
114 | v := []int{}
115 | info, err := reflection.GetObjectInfo(&v)
116 | if err != nil {
117 | t.Fatal()
118 | }
119 | t.Logf("classname :%v", info.GetClassName())
120 | t.Log(v)
121 | info.SetValue(reflect.ValueOf(1))
122 |
123 | t.Logf("after set int type :%v\n", v)
124 |
125 | info.SetValue(reflect.ValueOf([]float32{1.0, 2, 3}))
126 |
127 | t.Logf("after set []float32{1.0,2,3} :%v\n", v)
128 |
129 | info.SetValue(reflect.ValueOf([]int{1, 2, 3}))
130 |
131 | t.Logf("after set []int{1,2,3} :%v\n", v)
132 |
133 | info.SetField("username", reflect.ValueOf(123))
134 | t.Logf("after setField :%v\n", v)
135 |
136 | info.AddValue(reflect.ValueOf(123))
137 | t.Logf("after AddValue :%v\n", v)
138 | }
139 |
140 | func TestReflectObjectSlice(t *testing.T) {
141 | v := []TestTable{}
142 | info, err := reflection.GetObjectInfo(&v)
143 | if err != nil {
144 | t.Fatal()
145 | }
146 | t.Logf("classname :%v", info.GetClassName())
147 | t.Log(v)
148 | info.SetValue(reflect.ValueOf(1))
149 |
150 | t.Logf("after set int type :%v\n", v)
151 |
152 | info.SetValue(reflect.ValueOf([]float32{1.0, 2, 3}))
153 |
154 | t.Logf("after set []float32{1.0,2,3} :%v\n", v)
155 |
156 | info.SetValue(reflect.ValueOf([]TestTable{{Username: "1", Password: "1"}}))
157 |
158 | t.Logf(`after set []TestTable{{Username:"1", Password:"1"}} :%v\n`, v)
159 |
160 | info.SetField("username", reflect.ValueOf(123))
161 | t.Logf("after setField :%v\n", v)
162 |
163 | info.AddValue(reflect.ValueOf(1))
164 | t.Logf("after AddValue 1 :%v\n", v)
165 |
166 | info.AddValue(reflect.ValueOf(TestTable{Username: "2", Password: "2"}))
167 | t.Logf(`after AddValue TestTable{Username: "2", Password:"2"} :%v\n`, v)
168 |
169 | ev := info.NewElem()
170 | ev.SetField("username", "x")
171 | info.AddValue(ev.GetValue())
172 |
173 | t.Logf(`after AddValue new elem {Username: "x"} :%v\n`, v)
174 | }
175 |
--------------------------------------------------------------------------------
/test/postgresql/postgresql.tpl:
--------------------------------------------------------------------------------
1 | {{define "selectTestTable"}}
2 | {{$COLUMNS := "id, username, password"}}
3 | SELECT {{$COLUMNS}} FROM test_table
4 | {{where (ne .Username "") "AND" "\"username\" =" .Username "" | where (ne .Password "pw") "AND" "\"password\"=" .Password}}
5 | {{end}}
6 |
7 | {{define "insertTestTable"}}
8 | {{$COLUMNS := "id, username, password"}}
9 | INSERT INTO test_table ({{$COLUMNS}})
10 | VALUES(
11 | {{.Id}},
12 | '{{.Username}}',
13 | '{{.Password}}'
14 | )
15 | {{end}}
16 |
17 | {{define "insertBatchTestTable"}}
18 | {{$size := len . | add -1}}
19 | INSERT INTO "test_table"("id","username","password")
20 | VALUES {{range $i, $v := .}}
21 | ({{$v.Id}}, '{{$v.Username}}', '{{$v.Password}}'){{if lt $i $size}},{{end}}
22 | {{end}}
23 | {{end}}
24 |
25 | {{define "updateTestTable"}}
26 | UPDATE test_table
27 | {{set (ne .Username "") "\"username\" =" .Username "" | set (ne .Password "") "\"password\" =" .Password}}
28 | {{where (ne .Id 0) "AND" "\"id\"=" .Id ""}}
29 | {{end}}
30 |
31 | {{define "deleteTestTable"}}
32 | DELETE FROM test_table
33 | {{where (ne .Id 0) "AND" "id" .Id "" | where (ne .Username "") "AND" "\"username\"=" .Username | where (ne .Password "") "AND" "\"password\"=" .Password}}
34 | {{end}}
35 |
--------------------------------------------------------------------------------
/test/postgresql/postgresql.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | id,username,password
20 |
21 |
29 |
30 |
38 |
39 |
40 | INSERT INTO test_table (id,username,password)
41 | VALUES(
42 | #{TestTable.id},
43 | #{TestTable.username},
44 | #{TestTable.password}
45 | )
46 |
47 |
48 |
49 | UPDATE test_table
50 |
51 | username = #{TestTable.username}
52 | password = #{TestTable.password}
53 |
54 | WHERE id = #{TestTable.id}
55 |
56 |
57 |
58 | DELETE FROM test_table
59 |
60 | AND id = #{TestTable.id}
61 | AND username = #{TestTable.username}
62 | AND password = #{TestTable.password}
63 |
64 |
65 |
--------------------------------------------------------------------------------
/test/postgresql/postgresql2.tpl:
--------------------------------------------------------------------------------
1 | {{define "selectTestTable"}}
2 | {{$COLUMNS := "id, username, password"}}
3 | SELECT {{$COLUMNS}} FROM test_table WHERE 1=1
4 | {{if (ne .Username "")}} AND username = '{{.Username}}' {{end}}
5 | {{if (ne .Password "")}} AND password = '{{.Password}}' {{end}}
6 | {{end}}
7 |
8 | {{define "insertTestTable"}}
9 | {{$COLUMNS := "id, username, password"}}
10 | INSERT INTO test_table ({{$COLUMNS}})
11 | VALUES(
12 | {{.Id}},
13 | '{{.Username}}',
14 | '{{.Password}}'
15 | )
16 | {{end}}
17 |
18 | {{define "updateTestTable"}}
19 | UPDATE test_table SET id = id
20 | {{if (ne .Username "")}} , username = '{{.Username}}' {{end}}
21 | {{if (ne .Password "")}} , password = '{{.Password}}' {{end}}
22 | {{if (ne .Id 0)}} WHERE id = {{.Id}} {{end}}
23 | {{end}}
24 |
25 | {{define "deleteTestTable"}}
26 | DELETE FROM test_table WHERE 1=1
27 | {{if (ne .Id 0)}} AND id = {{.Id}} {{end}}
28 | {{if (ne .Username "")}} AND username = {{.Username}} {{end}}
29 | {{if (ne .Password "")}} AND password = {{.Password}} {{end}}
30 | {{end}}
31 |
--------------------------------------------------------------------------------
/test/postgresql/session_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | "testing"
23 | )
24 |
25 | func TestSessionTpl(t *testing.T) {
26 | gobatis.RegisterTemplateFile("./postgresql.tpl")
27 | var param = TestTable{Id: 1, Username: "user", Password: "pw"}
28 | t.Run("select", func(t *testing.T) {
29 | mgr := gobatis.NewSessionManager(connect())
30 | sess := mgr.NewSession()
31 | var ret []TestTable
32 | sess.Select("selectTestTable").Param(param).Result(&ret)
33 | t.Log(ret)
34 | })
35 |
36 | t.Run("insert", func(t *testing.T) {
37 | mgr := gobatis.NewSessionManager(connect())
38 | sess := mgr.NewSession()
39 | var ret int
40 | err := sess.Insert("insertTestTable").Param(param).Result(&ret)
41 | t.Log(err)
42 | t.Log(ret)
43 | })
44 |
45 | t.Run("insertBatch", func(t *testing.T) {
46 | mgr := gobatis.NewSessionManager(connect())
47 | sess := mgr.NewSession()
48 | var ret int
49 | err := sess.Insert("insertBatchTestTable").Param([]TestTable{
50 | {Id: 13, Username: "user13", Password: "pw13"},
51 | {Id: 14, Username: "user14", Password: "pw14"},
52 | }).Result(&ret)
53 | t.Log(err)
54 | t.Log(ret)
55 | })
56 |
57 | t.Run("update", func(t *testing.T) {
58 | mgr := gobatis.NewSessionManager(connect())
59 | sess := mgr.NewSession()
60 | var ret int
61 | err := sess.Update("updateTestTable").Param(TestTable{Id: 1, Username: "user2", Password: "pw2"}).Result(&ret)
62 | t.Log(err)
63 | t.Log(ret)
64 | })
65 |
66 | t.Run("delete", func(t *testing.T) {
67 | mgr := gobatis.NewSessionManager(connect())
68 | sess := mgr.NewSession()
69 | var ret int
70 | sess.Delete("deleteTestTable").Param(TestTable{Id: 1}).Result(&ret)
71 | t.Log(ret)
72 | })
73 | }
74 |
75 | func TestSessionXml(t *testing.T) {
76 | gobatis.RegisterMapperFile("./postgresql.xml")
77 | var param = TestTable{Id: 1, Username: "user", Password: "pw"}
78 | t.Run("select", func(t *testing.T) {
79 | mgr := gobatis.NewSessionManager(connect())
80 | sess := mgr.NewSession()
81 | var ret []TestTable
82 | sess.Select("selectTestTable").Param(param).Result(&ret)
83 | t.Log(ret)
84 | })
85 |
86 | t.Run("insert", func(t *testing.T) {
87 | mgr := gobatis.NewSessionManager(connect())
88 | sess := mgr.NewSession()
89 | var ret int
90 | err := sess.Insert("insertTestTable").Param(param).Result(&ret)
91 | t.Log(err)
92 | t.Log(ret)
93 | })
94 |
95 | t.Run("update", func(t *testing.T) {
96 | mgr := gobatis.NewSessionManager(connect())
97 | sess := mgr.NewSession()
98 | var ret int
99 | err := sess.Update("updateTestTable").Param(TestTable{Id: 1, Username: "user2", Password: "pw2"}).Result(&ret)
100 | t.Log(err)
101 | t.Log(ret)
102 | })
103 |
104 | t.Run("delete", func(t *testing.T) {
105 | mgr := gobatis.NewSessionManager(connect())
106 | sess := mgr.NewSession()
107 | var ret int
108 | sess.Delete("deleteTestTable").Param(TestTable{Id: 1}).Result(&ret)
109 | t.Log(ret)
110 | })
111 | }
112 |
--------------------------------------------------------------------------------
/test/runner_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | //_ "github.com/go-sql-driver/mysql"
22 | "github.com/acmestack/gobatis"
23 | "github.com/acmestack/gobatis/datasource"
24 | "github.com/acmestack/gobatis/factory"
25 | "testing"
26 | )
27 |
28 | type TestTable struct {
29 | TestTable gobatis.TableName "test_table"
30 | Id int64 `column:"id"`
31 | Username string `column:"username"`
32 | Password string `column:"password"`
33 | }
34 |
35 | func connect() factory.Factory {
36 | return gobatis.NewFactory(
37 | gobatis.SetMaxConn(100),
38 | gobatis.SetMaxIdleConn(50),
39 | gobatis.SetDataSource(&datasource.MysqlDataSource{
40 | Host: "localhost",
41 | Port: 3306,
42 | DBName: "test",
43 | Username: "root",
44 | Password: "123",
45 | Charset: "utf8",
46 | }))
47 | }
48 |
49 | func TestSelectgobatis(t *testing.T) {
50 | var testV TestTable
51 |
52 | mgr := gobatis.NewSessionManager(connect())
53 | gobatis.RegisterSql("queryTest", "select * from test_table where id = #{0}")
54 | gobatis.RegisterModel(&testV)
55 | mgr.NewSession().Select("queryTest").Param(1, 200, 300).Result(&testV)
56 |
57 | t.Logf("%v %v", testV.Username, testV.Password)
58 | }
59 |
60 | func TestSelectgobatis2(t *testing.T) {
61 | var testV TestTable
62 |
63 | mgr := gobatis.NewSessionManager(connect())
64 | gobatis.RegisterSql("queryTest", "select * from test_table limit 10")
65 | gobatis.RegisterModel(&testV)
66 | testList := []TestTable{}
67 | mgr.NewSession().Select("queryTest").Param().Result(&testList)
68 |
69 | for _, v := range testList {
70 | t.Logf("%v %v", v.Username, v.Password)
71 | }
72 | }
73 |
74 | func TestSelectgobatis3(t *testing.T) {
75 | var testV TestTable
76 |
77 | mgr := gobatis.NewSessionManager(connect())
78 | gobatis.RegisterSql("queryTest", "select count(*) from test_table")
79 | gobatis.RegisterModel(&testV)
80 | i := 0
81 | mgr.NewSession().Select("queryTest").Param().Result(&i)
82 | }
83 |
84 | func TestInsertgobatis(t *testing.T) {
85 | var testV TestTable
86 |
87 | mgr := gobatis.NewSessionManager(connect())
88 | gobatis.RegisterSql("insertTest", "insert into test_table (username, password) value(#{username}, #{password})")
89 | gobatis.RegisterModel(&testV)
90 | testV.Username = "test_user"
91 | testV.Password = "test_pw"
92 | i := 0
93 | mgr.NewSession().Insert("insertTest").Param(testV).Result(&i)
94 | t.Logf("insert %d\n", i)
95 | }
96 |
97 | func TestUpdategobatis(t *testing.T) {
98 | var testV TestTable
99 |
100 | mgr := gobatis.NewSessionManager(connect())
101 | gobatis.RegisterSql("updateTest", "update test_table set username = #{username}, password = #{password} where id = 1")
102 | gobatis.RegisterModel(&testV)
103 | testV.Username = "test_user"
104 | testV.Password = "test_pw"
105 | i := 0
106 | mgr.NewSession().Update("updateTest").Param(testV).Result(&i)
107 | t.Logf("update %d\n", i)
108 | }
109 |
110 | func TestDeletegobatis(t *testing.T) {
111 | var testV TestTable
112 |
113 | mgr := gobatis.NewSessionManager(connect())
114 | gobatis.RegisterSql("deleteTest", "delete from test_table where username = #{username}, password = #{password}")
115 | gobatis.RegisterModel(&testV)
116 | testV.Username = "test_user"
117 | testV.Password = "test_pw"
118 | i := 0
119 | mgr.NewSession().Delete("deleteTest").Param(testV).Result(&i)
120 | t.Logf("delete %d\n", i)
121 | }
122 |
123 | func TestDynamicSelectgobatis(t *testing.T) {
124 | var testV TestTable
125 |
126 | mgr := gobatis.NewSessionManager(connect())
127 | gobatis.RegisterSql("deleteTest", `select id from test_table
128 |
129 |
130 | username = #{username}, password = #{password}
131 | `)
132 | gobatis.RegisterModel(&testV)
133 | testV.Username = "test_user"
134 | testV.Password = "test_pw"
135 | i := 0
136 | mgr.NewSession().Delete("deleteTest").Param(testV).Result(&i)
137 | t.Logf("delete %d\n", i)
138 | }
139 |
140 | func TestTx1(t *testing.T) {
141 | mgr := gobatis.NewSessionManager(connect())
142 | testV := TestTable{
143 | Username: "testuser",
144 | Password: "testpw",
145 | }
146 | gobatis.RegisterModel(&testV)
147 | gobatis.RegisterSql("insert_id", "insert into test_table (username, password) value (#{username}, #{password})")
148 | gobatis.RegisterSql("select_id", "select * from test_table")
149 |
150 | var testList []TestTable
151 |
152 | mgr.NewSession().Tx(func(session *gobatis.Session) error {
153 | ret := 0
154 | err := session.Insert("insert_id").Param(testV).Result(&ret)
155 | if err != nil {
156 | return err
157 | }
158 | t.Logf("ret %d\n", ret)
159 | session.Select("select_id").Param().Result(&testList)
160 | for _, v := range testList {
161 | t.Logf("data: %v", v)
162 | }
163 | //commit
164 | return nil
165 | })
166 | }
167 |
168 | func TestTx2(t *testing.T) {
169 | mgr := gobatis.NewSessionManager(connect())
170 | testV := TestTable{
171 | Username: "testuser",
172 | Password: "testpw",
173 | }
174 | gobatis.RegisterModel(&testV)
175 | gobatis.RegisterSql("insert_id", "insert into test_table (username, password) value (#{username}, #{password})")
176 | gobatis.RegisterSql("select_id", "select * from test_table")
177 |
178 | var testList []TestTable
179 |
180 | mgr.NewSession().Tx(func(session *gobatis.Session) error {
181 | ret := 0
182 | err := session.Insert("insert_id").Param(testV).Result(&ret)
183 | if err != nil {
184 | t.Logf("error %v\n", err)
185 | return err
186 | }
187 | t.Logf("ret %d\n", ret)
188 | session.Select("select_id").Param().Result(&testList)
189 | for _, v := range testList {
190 | t.Logf("data: %v", v)
191 | }
192 | //rollback
193 | panic("ROLLBACK panic!!")
194 |
195 | //rollback
196 | return nil
197 | })
198 | }
199 |
--------------------------------------------------------------------------------
/test/sql_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | "github.com/acmestack/gobatis/datasource"
23 | _ "github.com/go-sql-driver/mysql"
24 | "testing"
25 | )
26 |
27 | type SqlTest struct {
28 | Id int64 `column:"id"`
29 | Username string `column:"username"`
30 | Password string `column:"password"`
31 | }
32 |
33 | var sessionMgr *gobatis.SessionManager
34 |
35 | func init() {
36 | fac := gobatis.NewFactory(
37 | gobatis.SetMaxConn(100),
38 | gobatis.SetMaxIdleConn(50),
39 | gobatis.SetDataSource(&datasource.MysqlDataSource{
40 | Host: "localhost",
41 | Port: 3306,
42 | DBName: "test",
43 | Username: "root",
44 | Password: "123",
45 | Charset: "utf8",
46 | }))
47 | var testV TestTable
48 | gobatis.RegisterModel(&testV)
49 | sessionMgr = gobatis.NewSessionManager(fac)
50 | }
51 |
52 | func TestSelectWithSimpleType(t *testing.T) {
53 | sql := `SELECT username, password FROM test_table WHERE id = #{0}`
54 | ret := SqlTest{}
55 | sessionMgr.NewSession().Select(sql).Param(1).Result(&ret)
56 | t.Logf("%v %v", ret.Username, ret.Password)
57 | }
58 |
59 | func TestSelectWithMap(t *testing.T) {
60 | sql := `SELECT username, password FROM test_table WHERE id = #{id}`
61 | ret := SqlTest{}
62 | sessionMgr.NewSession().Select(sql).Param(map[string]interface{}{"id": 1}).Result(&ret)
63 | t.Logf("%v %v", ret.Username, ret.Password)
64 | }
65 |
66 | func TestSelectWithStruct(t *testing.T) {
67 | sql := `SELECT username, password FROM test_table WHERE id = #{SqlTest.id}`
68 | ret := SqlTest{}
69 | sessionMgr.NewSession().Select(sql).Param(SqlTest{Id: 1}).Result(&ret)
70 | t.Logf("%v %v", ret.Username, ret.Password)
71 | }
72 |
73 | func TestSelectWithComplex(t *testing.T) {
74 | sql := `SELECT username, password FROM test_table WHERE id = #{SqlTest.id} AND username = #{0} AND password = #{pw}`
75 | ret := SqlTest{}
76 | sessionMgr.NewSession().Select(sql).Param(SqlTest{Id: 1}, "test_user", map[string]interface{}{"pw": "test_pw"}).Result(&ret)
77 | t.Logf("%v %v", ret.Username, ret.Password)
78 | }
79 |
--------------------------------------------------------------------------------
/test/sqlbuilder_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis/builder"
22 | "strings"
23 | "testing"
24 | )
25 |
26 | func TestSqlBuilderSelect(t *testing.T) {
27 | hook := func(f *builder.SQLFragment) *builder.SQLFragment {
28 | t.Log(f)
29 | return f
30 | }
31 | t.Run("once call", func(t *testing.T) {
32 | str := builder.Select("test1", "test2").
33 | Hook(hook).
34 | From("test_a").
35 | Hook(hook).
36 | Where("id = 1").
37 | And().
38 | Hook(hook).
39 | Where("name=2").
40 | Hook(hook).
41 | GroupBy("name").
42 | Hook(hook).
43 | OrderBy("name").
44 | Hook(hook).
45 | Desc().
46 | Hook(hook).
47 | Limit(5, 10).
48 | Hook(hook).
49 | String()
50 | t.Log(str)
51 |
52 | if strings.TrimSpace(str) != `SELECT test1, test2 FROM test_a WHERE id = 1 AND name=2 GROUP BY name ORDER BY name DESC LIMIT 5, 10` {
53 | t.FailNow()
54 | }
55 | })
56 |
57 | t.Run("multi call", func(t *testing.T) {
58 | str := builder.Select("A.test1", "B.test2").
59 | Select("B.test3").
60 | From("test_a AS A").
61 | From("test_b AS B").
62 | Where("id = 1").
63 | And().
64 | Where("name=2").
65 | GroupBy("name").
66 | OrderBy("name").
67 | Desc().
68 | Limit(5, 10).
69 | String()
70 | t.Log(str)
71 |
72 | if strings.TrimSpace(str) != `SELECT A.test1, B.test2, B.test3 FROM test_a AS A, test_b AS B WHERE id = 1 AND name=2 GROUP BY name ORDER BY name DESC LIMIT 5, 10` {
73 | t.FailNow()
74 | }
75 | })
76 | }
77 |
78 | func TestSqlBuilderInsert(t *testing.T) {
79 | str := builder.InsertInto("test_table").
80 | IntoColumns("a", "b").
81 | IntoColumns("c").
82 | IntoValues("#{0}, #{1}").
83 | IntoValues("#{3}").
84 | String()
85 | t.Log(str)
86 |
87 | if strings.TrimSpace(str) != `INSERT INTO test_table (a, b, c) VALUES(#{0}, #{1}, #{3})` {
88 | t.FailNow()
89 | }
90 | }
91 |
92 | func TestSqlBuilderUpdate(t *testing.T) {
93 | str := builder.Update("test_table").
94 | Set("a", "#{0}").
95 | Set("b", "#{1}").
96 | Where("id = #{3}").
97 | Or().
98 | Where("name = #{4}").
99 | String()
100 | t.Log(str)
101 | if strings.TrimSpace(str) != `UPDATE test_table SET a = #{0} , b = #{1} WHERE id = #{3} OR name = #{4}` {
102 | t.FailNow()
103 | }
104 | }
105 |
106 | func TestSqlBuilderDelete(t *testing.T) {
107 | str := builder.DeleteFrom("test_table").
108 | Where("id = #{3}").
109 | Or().
110 | Where("name = #{4}").
111 | String()
112 | t.Log(str)
113 | if strings.TrimSpace(str) != `DELETE FROM test_table WHERE id = #{3} OR name = #{4}` {
114 | t.FailNow()
115 | }
116 | }
117 |
118 | func TestSqlBuilderWhere(t *testing.T) {
119 | str := builder.DeleteFrom("test_table").
120 | Or().
121 | Where("name = #{4}").
122 | String()
123 | t.Log(str)
124 |
125 | if strings.TrimSpace(str) != `DELETE FROM test_table WHERE name = #{4}` {
126 | t.FailNow()
127 | }
128 | }
129 |
130 | func TestSqlBuilderError(t *testing.T) {
131 | f := builder.InsertInto("test_table")
132 | f.IntoColumns("a").IntoValues("#{0}").IntoValues("a").IntoColumns("a")
133 |
134 | t.Log(f.String())
135 | }
136 |
--------------------------------------------------------------------------------
/test/sqlite/runner_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "database/sql"
22 | "errors"
23 | "fmt"
24 | "github.com/acmestack/gobatis"
25 | "github.com/acmestack/gobatis/datasource"
26 | "github.com/acmestack/gobatis/factory"
27 | _ "github.com/mattn/go-sqlite3"
28 | "testing"
29 | )
30 |
31 | type TestTable struct {
32 | TestTable gobatis.TableName "test_table"
33 | Id int64 `column:"id"`
34 | Username string `column:"username"`
35 | Password string `column:"password"`
36 | }
37 |
38 | func (t *TestTable) String() string {
39 | return fmt.Sprintf("id: %d, u: %s, p: %s", t.Id, t.Username, t.Password)
40 | }
41 |
42 | func connect() factory.Factory {
43 | return gobatis.NewFactory(
44 | gobatis.SetMaxConn(100),
45 | gobatis.SetMaxIdleConn(50),
46 | gobatis.SetDataSource(&datasource.SqliteDataSource{
47 | Path: "test.db",
48 | }))
49 | }
50 |
51 | func initTest(t *testing.T) (err error) {
52 | sql_table := "CREATE TABLE IF NOT EXISTS `test_table` (" +
53 | "`id` INTEGER PRIMARY KEY AUTOINCREMENT," +
54 | "`username` VARCHAR(64) NULL," +
55 | "`password` VARCHAR(64) NULL," +
56 | "`createTime` TIMESTAMP default (datetime('now', 'localtime')));"
57 |
58 | db, err := sql.Open("sqlite3", "./test.db")
59 | if err != nil {
60 | t.Fatal(err)
61 | }
62 | defer db.Close()
63 |
64 | _, err = db.Exec(sql_table)
65 | if err != nil {
66 | t.Fatal(err)
67 | }
68 |
69 | _, err = db.Exec("DELETE FROM test_table")
70 | if err != nil {
71 | t.Fatal(err)
72 | }
73 | return nil
74 | }
75 |
76 | func TestSqlite3(t *testing.T) {
77 | initTest(t)
78 | var testV TestTable
79 |
80 | mgr := gobatis.NewSessionManager(connect())
81 | sess := mgr.NewSession()
82 | count := 0
83 | errI := sess.Insert("INSERT INTO test_table(username, password) VALUES('user1', 'pw1')").Param().Result(&count)
84 | if errI != nil {
85 | t.Fatal(errI)
86 | }
87 | testV = TestTable{}
88 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
89 | t.Logf("after insert: %d %v\n", count, testV)
90 | if count != 1 || testV.Username != "user1" || testV.Password != "pw1" {
91 | t.Fail()
92 | }
93 |
94 | errU := sess.Update("Update test_table SET username = 'user2', password = 'pw2'").Param().Result(&count)
95 | if errU != nil {
96 | t.Fatal(errU)
97 | }
98 | testV = TestTable{}
99 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
100 | t.Logf("after update: %d %v\n", count, testV)
101 | if count != 1 || testV.Username != "user2" || testV.Password != "pw2" {
102 | t.Fail()
103 | }
104 |
105 | errD := sess.Delete("DELETE FROM test_table").Param().Result(&count)
106 | if errD != nil {
107 | t.Fatal(errD)
108 | }
109 | testV = TestTable{}
110 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
111 | t.Logf("after delete: %d %v\n", count, testV)
112 | if count != 1 || testV.Username != "" || testV.Password != "" {
113 | t.Fail()
114 | }
115 |
116 | t.Logf("%v %v", testV.Username, testV.Password)
117 | }
118 |
119 | func TestSqliteTx(t *testing.T) {
120 | initTest(t)
121 | var testV TestTable
122 |
123 | mgr := gobatis.NewSessionManager(connect())
124 | session := mgr.NewSession()
125 | session.Tx(func(sess *gobatis.Session) error {
126 | count := 0
127 | errI := sess.Insert("INSERT INTO test_table(username, password) VALUES('user1', 'pw1')").Param().Result(&count)
128 | if errI != nil {
129 | t.Fatal(errI)
130 | }
131 | testV = TestTable{}
132 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
133 | t.Logf("after insert: %d %v\n", count, testV)
134 | if count != 1 || testV.Username != "user1" || testV.Password != "pw1" {
135 | t.Fail()
136 | }
137 |
138 | errU := sess.Update("Update test_table SET username = 'user2', password = 'pw2'").Param().Result(&count)
139 | if errU != nil {
140 | t.Fatal(errU)
141 | }
142 | testV = TestTable{}
143 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
144 | t.Logf("after update: %d %v\n", count, testV)
145 | if count != 1 || testV.Username != "user2" || testV.Password != "pw2" {
146 | t.Fail()
147 | }
148 |
149 | errD := sess.Delete("DELETE FROM test_table").Param().Result(&count)
150 | if errD != nil {
151 | t.Fatal(errD)
152 | }
153 | testV = TestTable{}
154 | sess.Select("SELECT * FROM test_table").Param().Result(&testV)
155 | t.Logf("after delete: %d %v\n", count, testV)
156 | if count != 1 || testV.Username != "" || testV.Password != "" {
157 | t.Fail()
158 | }
159 | return nil
160 | })
161 | }
162 |
163 | func TestTx1(t *testing.T) {
164 | initTest(t)
165 | mgr := gobatis.NewSessionManager(connect())
166 | testV := TestTable{}
167 | gobatis.RegisterModel(&testV)
168 | gobatis.RegisterSql("insert_id", "insert into test_table (id, username, password) "+
169 | "values (#{TestTable.id}, #{TestTable.username}, #{TestTable.password})")
170 | gobatis.RegisterSql("select_id", "select * from test_table where id = #{TestTable.id}")
171 |
172 | var retVar TestTable
173 |
174 | mgr.NewSession().Tx(func(session *gobatis.Session) error {
175 | ret := 0
176 | err := session.Insert("insert_id").Param(TestTable{Id: 40, Username: "user", Password: "pw"}).Result(&ret)
177 | if err != nil {
178 | return err
179 | }
180 | t.Logf("ret %d\n", ret)
181 | session.Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
182 | t.Logf("data: %v", retVar)
183 | //commit
184 | return nil
185 | })
186 |
187 | retVar = TestTable{}
188 | mgr.NewSession().Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
189 | t.Logf("out tx: %v", retVar)
190 | if retVar.Username != "user" || retVar.Password != "pw" {
191 | t.Fail()
192 | }
193 | }
194 |
195 | func TestTx2(t *testing.T) {
196 | initTest(t)
197 | mgr := gobatis.NewSessionManager(connect())
198 | testV := TestTable{}
199 | gobatis.RegisterModel(&testV)
200 | gobatis.RegisterSql("insert_id", "insert into test_table (id, username, password) "+
201 | "values (#{TestTable.id}, #{TestTable.username}, #{TestTable.password})")
202 | gobatis.RegisterSql("select_id", "select * from test_table where id = #{TestTable.id}")
203 |
204 | var retVar TestTable
205 |
206 | mgr.NewSession().Tx(func(session *gobatis.Session) error {
207 | ret := 0
208 | err := session.Insert("insert_id").Param(TestTable{Id: 40, Username: "user", Password: "pw"}).Result(&ret)
209 | if err != nil {
210 | return err
211 | }
212 | t.Logf("ret %d\n", ret)
213 | session.Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
214 | t.Logf("data: %v", retVar)
215 | //commit
216 | return errors.New("rollback! ")
217 | })
218 |
219 | retVar = TestTable{}
220 | mgr.NewSession().Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
221 | t.Logf("out tx: %v", retVar)
222 | if retVar.Username == "user" || retVar.Password == "pw" {
223 | t.Fail()
224 | }
225 | }
226 |
227 | func TestTx3(t *testing.T) {
228 | initTest(t)
229 | mgr := gobatis.NewSessionManager(connect())
230 | testV := TestTable{}
231 | gobatis.RegisterModel(&testV)
232 | gobatis.RegisterSql("insert_id", "insert into test_table (id, username, password) "+
233 | "values (#{TestTable.id}, #{TestTable.username}, #{TestTable.password})")
234 | gobatis.RegisterSql("select_id", "select * from test_table where id = #{TestTable.id}")
235 |
236 | var retVar TestTable
237 |
238 | defer func() {
239 | if r := recover(); r != nil {
240 | retVar = TestTable{}
241 | mgr.NewSession().Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
242 | t.Logf("out tx: %v", retVar)
243 | if retVar.Username == "user" || retVar.Password == "pw" {
244 | t.Fail()
245 | }
246 | }
247 | }()
248 | mgr.NewSession().Tx(func(session *gobatis.Session) error {
249 | ret := 0
250 | err := session.Insert("insert_id").Param(TestTable{Id: 40, Username: "user", Password: "pw"}).Result(&ret)
251 | if err != nil {
252 | return err
253 | }
254 | t.Logf("ret %d\n", ret)
255 | session.Select("select_id").Param(TestTable{Id: 40}).Result(&retVar)
256 | t.Logf("data: %v", retVar)
257 | panic("rollback! ")
258 | //commit
259 | return nil
260 | })
261 | }
262 |
--------------------------------------------------------------------------------
/test/sqlmanager/manager_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package sqlmanager
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | "testing"
23 | )
24 |
25 | func TestManager(t *testing.T) {
26 | err := gobatis.ScanMapperFile("./x")
27 | if err != nil {
28 | t.Fatal(err)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/sqlmanager/x/tpl/x.tpl:
--------------------------------------------------------------------------------
1 | {{define "namespace"}}test{{end}}
2 |
3 | {{define "selectTestTable"}}
4 | SELECT id,username,password,createtime FROM test_table
5 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
6 | {{end}}
7 |
8 | {{define "selectTestTableCount"}}
9 | SELECT COUNT(*) FROM test_table
10 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
11 | {{end}}
12 |
13 | {{define "insertTestTable"}}
14 | INSERT INTO test_table(id,username,password,createtime)
15 | VALUES(
16 | {{arg .Id}}, {{arg .Username}}, {{arg .Password}}, {{arg .Createtime}})
17 | {{end}}
18 |
19 | {{define "insertBatchTestTable"}}
20 | {{$size := len . | add -1}}
21 | INSERT INTO test_table(id,username,password,createtime)
22 | VALUES {{range $i, $v := .}}
23 | ({{arg $v.Id}}, {{arg $v.Username}}, {{arg $v.Password}}, {{arg $v.Createtime}}){{if lt $i $size}},{{end}}
24 | {{end}}
25 | {{end}}
26 |
27 | {{define "updateTestTable"}}
28 | UPDATE test_table
29 | {{set .Id "id = " (arg .Id) "" | set .Username "username = " (arg .Username) | set .Password "password = " (arg .Password) | set .Createtime "createtime = " (arg .Createtime)}}
30 | {{where .Id "AND" "id = " (arg .Id) ""}}
31 | {{end}}
32 |
33 | {{define "deleteTestTable"}}
34 | DELETE FROM test_table
35 | {{where .Id "AND" "id = " (arg .Id) "" | where .Username "AND" "username = " (arg .Username) | where .Password "AND" "password = " (arg .Password) | where .Createtime "AND" "createtime = " (arg .Createtime)}}
36 | {{end}}
--------------------------------------------------------------------------------
/test/sqlmanager/x/xml/x.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | id,username,password,createtime
20 |
21 |
30 |
31 |
40 |
41 |
42 | INSERT INTO test_table (id,username,password,createtime)
43 | VALUES(
44 | #{TestTable.id},
45 | #{TestTable.username},
46 | #{TestTable.password},
47 | #{TestTable.createtime}
48 | )
49 |
50 |
51 |
52 | INSERT INTO test_table (id,username,password,createtime)
53 | VALUES
54 |
55 | (#{item.TestTable.id},#{item.TestTable.username},#{item.TestTable.password},#{item.TestTable.createtime})
56 |
57 |
58 |
59 |
60 | UPDATE test_table
61 |
62 | username = #{TestTable.username}
63 | password = #{TestTable.password}
64 | createtime = #{TestTable.createtime}
65 |
66 | WHERE id = #{TestTable.id}
67 |
68 |
69 |
70 | DELETE FROM test_table
71 |
72 | AND id = #{TestTable.id}
73 | AND username = #{TestTable.username}
74 | AND password = #{TestTable.password}
75 | AND createtime = #{TestTable.createtime}
76 |
77 |
78 |
--------------------------------------------------------------------------------
/test/sqlparse_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | "github.com/acmestack/gobatis/parsing/sqlparser"
23 | "github.com/acmestack/gobatis/reflection"
24 | "testing"
25 | "time"
26 | )
27 |
28 | func TestSqlParser1(t *testing.T) {
29 | sqlStr := "SELECT * from xxx WHERE id = #{id}, name = #{name}"
30 | ret, _ := sqlparser.SimpleParse(sqlStr)
31 | t.Log(ret.String())
32 | if ret.Action != sqlparser.SELECT {
33 | t.Fail()
34 | }
35 |
36 | if ret.Vars[0] != "id" {
37 | t.Fail()
38 | }
39 |
40 | if ret.Vars[1] != "name" {
41 | t.Fail()
42 | }
43 | }
44 |
45 | func TestSqlParser2(t *testing.T) {
46 | sqlStr := "SELECT * from xxx WHERE id = #{id"
47 | _, err := sqlparser.SimpleParse(sqlStr)
48 | if err == nil {
49 | t.Fail()
50 | } else {
51 | t.Log(err)
52 | }
53 | }
54 |
55 | func TestSqlParser3(t *testing.T) {
56 | sqlStr := "SELECT * from xxx WHERE id = #{id, name = #{name}"
57 | _, err := sqlparser.SimpleParse(sqlStr)
58 | if err == nil {
59 | t.Fail()
60 | } else {
61 | t.Log(err)
62 | }
63 | }
64 |
65 | func TestSqlParser4(t *testing.T) {
66 | sqlStr := "DELETE from xxx WHERE id = #{id}, name = #{name}"
67 | ret, _ := sqlparser.SimpleParse(sqlStr)
68 | t.Log(ret.String())
69 | if ret.Action != sqlparser.DELETE {
70 | t.Fail()
71 | }
72 |
73 | if ret.Vars[0] != "id" {
74 | t.Fail()
75 | }
76 |
77 | if ret.Vars[1] != "name" {
78 | t.Fail()
79 | }
80 | }
81 |
82 | func TestSqlParserWithParams1(t *testing.T) {
83 | sqlStr := "DELETE from xxx WHERE id = #{0}, name = #{1}, id = #{0}"
84 | ret, _ := sqlparser.ParseWithParams(sqlStr, 123, "abc")
85 | t.Log(ret.String())
86 | if ret.Action != sqlparser.DELETE {
87 | t.Fail()
88 | }
89 |
90 | if ret.Vars[0] != "0" {
91 | t.Fail()
92 | }
93 |
94 | if ret.Vars[1] != "1" {
95 | t.Fail()
96 | }
97 | }
98 |
99 | func TestSqlParserWithParams2(t *testing.T) {
100 | sqlStr := "DELETE from ${2} WHERE id = ${0}, name = #{1}, id = #{0}"
101 | ret, _ := sqlparser.ParseWithParams(sqlStr, 123, "abc", "test_table")
102 | t.Log(ret.String())
103 | if ret.Action != sqlparser.DELETE {
104 | t.Fail()
105 | }
106 |
107 | if ret.Vars[0] != "2" {
108 | t.Fail()
109 | }
110 |
111 | if ret.Vars[1] != "0" {
112 | t.Fail()
113 | }
114 | }
115 |
116 | func TestSqlParserWithParams3(t *testing.T) {
117 | sqlStr := "SELECT from ${2} WHERE id = ${0, name = #{1}, id = #{0}"
118 | _, err := sqlparser.ParseWithParams(sqlStr, 123, "abc", "test_table")
119 | if err == nil {
120 | t.Fail()
121 | } else {
122 | t.Log(err)
123 | }
124 | }
125 |
126 | func TestSqlParserWithParams4(t *testing.T) {
127 | sqlStr := "SELECT from ${2} WHERE id = ${0}, name = #{1}, id = #{0}"
128 | _, err := sqlparser.ParseWithParams(sqlStr, 123, "abc")
129 | if err == nil {
130 | t.Fail()
131 | } else {
132 | t.Log(err)
133 | }
134 | }
135 |
136 | func TestSqlParserWithParamMap1(t *testing.T) {
137 | sqlStr := "SELECT * from ${tablename} WHERE id = ${id}, name = #{name}, id = #{id}"
138 | params := map[string]interface{}{
139 | "tablename": "test_table",
140 | "id": 123,
141 | "name": "test_name",
142 | }
143 | ret, _ := sqlparser.ParseWithParamMap("mysql", sqlStr, params)
144 | t.Log(ret.String())
145 | if ret.Action != sqlparser.SELECT {
146 | t.Fail()
147 | }
148 | }
149 |
150 | func TestSqlParserWithParamMap2(t *testing.T) {
151 | sqlStr := "SELECT * from ${tablename} WHERE id = ${id, name = #{name}, id = #{id}"
152 | params := map[string]interface{}{
153 | "tablename": "test_table",
154 | "id": 123,
155 | "name": "test_name",
156 | }
157 | _, err := sqlparser.ParseWithParamMap("mysql", sqlStr, params)
158 | if err == nil {
159 | t.Fail()
160 | } else {
161 | t.Log(err)
162 | }
163 | }
164 |
165 | func TestSqlParserWithParamMap3(t *testing.T) {
166 | sqlStr := "SELECT * from ${tablename} WHERE id = ${id}, name = #{name}, id = #{id}"
167 | params := map[string]interface{}{
168 | "tablename": "test_table",
169 | "id": 123,
170 | //"name" : "test_name",
171 | }
172 | _, err := sqlparser.ParseWithParamMap("mysql", sqlStr, params)
173 | if err == nil {
174 | t.Fail()
175 | } else {
176 | t.Log(err)
177 | }
178 | }
179 |
180 | type TestSqlParserStruct struct {
181 | TestTable gobatis.TableName "test_table"
182 | Id int `column:"id"`
183 | Name string `column:"name"`
184 | }
185 |
186 | func TestSqlParserWithParamMap4(t *testing.T) {
187 | sqlStr := "SELECT * from ${tablename} WHERE id = ${id}, name = #{name}, id = #{id}"
188 | paramVar := TestSqlParserStruct{
189 | Id: 123,
190 | Name: "test_name",
191 | }
192 | ti, _ := reflection.GetStructInfo(¶mVar)
193 | params := ti.MapValue()
194 | params["tablename"] = ti.Name
195 |
196 | ret, _ := sqlparser.ParseWithParamMap("mysql", sqlStr, params)
197 | t.Log(ret.String())
198 | if ret.Action != sqlparser.SELECT {
199 | t.Fail()
200 | }
201 | }
202 |
203 | func TestSqlParserWithTime1(t *testing.T) {
204 | sqlStr := "SELECT * from test_table WHERE time = ${0}"
205 | ret, _ := sqlparser.ParseWithParams(sqlStr, time.Time{})
206 | t.Log(ret.String())
207 | if ret.Action != sqlparser.SELECT {
208 | t.Fail()
209 | }
210 | }
211 |
212 | func TestSqlParserWithTime2(t *testing.T) {
213 | sqlStr := "SELECT * from test_table WHERE time > #{0}"
214 | ret, _ := sqlparser.ParseWithParams(sqlStr, time.Time{})
215 | t.Log(ret.String())
216 | if ret.Action != sqlparser.SELECT {
217 | t.Fail()
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/test/template/sql.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | {{define "namespace"}}test{{end}}
3 | */}}
4 |
5 | {{define "selectTestTable"}}
6 | {{$COLUMNS := "`id`, `username`, `password`"}}
7 | SELECT {{$COLUMNS}} FROM `TEST_TABLE`
8 | {{where (ne .UserName "") "AND" "\"username\"=" (arg .UserName) "" | where (ne .Password "pw") "AND" "\"password\"=" (arg .Password) | where (ne .Status -1) "AND" "\"status\"=" (arg .Status) | where .Time "AND" "\"time\"=" (arg .Time) }}
9 | {{end}}
10 |
11 | {{define "insertTestTable"}}
12 | {{$COLUMNS := "`id`, `username`, `password`"}}
13 | INSERT INTO `TEST_TABLE` ({{$COLUMNS}})
14 | VALUES(
15 | {{.UserName}},
16 | {{.Password}}
17 | )
18 | {{end}}
19 |
20 | {{define "insertBatchTestTable"}}
21 | {{$COLUMNS := "id, username, password"}}
22 | {{$size := len . | add -1}}
23 | INSERT INTO test_table ({{$COLUMNS}})
24 | VALUES {{range $i, $v := .}}
25 | ({{arg $v.Id}}, {{arg $v.UserName}}, {{arg $v.Password}}){{if lt $i $size}},{{end}}
26 | {{end}}
27 | {{end}}
28 |
29 | {{define "updateTestTable"}}
30 | UPDATE `TEST_TABLE`
31 | {{set (ne .UserName "") "\"username\"=" .UserName "" | set (ne .Password "") "\"password\"=" .Password | set (ne .Status -1) "\"status\"=" .Status}}
32 | {{if (ne .Id 0)}} WHERE `id` = {{.Id}} {{end}}
33 | {{end}}
34 |
35 | {{define "deleteTestTable"}}
36 | DELETE FROM `TEST_TABLE`
37 | {{where (ne .Id 0) "AND" "\"id\"=" .Id "" | where (ne .UserName "") "AND" "\"username\"=" .UserName | where (ne .Password "pw") "AND" "\"password\"=" .Password | where (ne .Status -1) "AND" "\"status\"=" .Status }}
38 | {{end}}
39 |
40 | {{template "selectTestTable"}}
41 | {{template "insertTestTable"}}
42 | {{template "updateTestTable"}}
43 | {{template "deleteTestTable"}}
--------------------------------------------------------------------------------
/test/template/sql_v2.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | {{define "namespace"}}test{{end}}
3 | */}}
4 |
5 | {{define "selectTestTable"}}
6 | {{$COLUMNS := "`id`, `username`, `password`"}}
7 | SELECT {{$COLUMNS}} FROM `TEST_TABLE`
8 | {{where .UserName "AND" "\"username\"=" (arg .UserName) "" | where (ne .Password "pw") "AND" "\"password\"=" (arg .Password) | where (ne .Status -1) "AND" "\"status\"=" (arg .Status) | where .Time "AND" "\"time\"=" (arg .Time) }}
9 | {{end}}
10 |
11 | {{define "insertTestTable"}}
12 | {{$COLUMNS := "`id`, `username`, `password`"}}
13 | INSERT INTO `TEST_TABLE` ({{$COLUMNS}})
14 | VALUES(
15 | {{.UserName}},
16 | {{.Password}}
17 | )
18 | {{end}}
19 |
20 | {{define "insertBatchTestTable"}}
21 | {{$COLUMNS := "id, username, password"}}
22 | {{$size := len . | add -1}}
23 | INSERT INTO test_table ({{$COLUMNS}})
24 | VALUES {{range $i, $v := .}}
25 | ({{arg $v.Id}}, {{arg $v.UserName}}, {{arg $v.Password}}){{if lt $i $size}},{{end}}
26 | {{end}}
27 | {{end}}
28 |
29 | {{define "updateTestTable"}}
30 | UPDATE `TEST_TABLE`
31 | {{set (ne .UserName "") "\"username\"=" .UserName "" | set (ne .Password "") "\"password\"=" .Password | set (ne .Status -1) "\"status\"=" .Status}}
32 | {{if (ne .Id 0)}} WHERE `id` = {{.Id}} {{end}}
33 | {{end}}
34 |
35 | {{define "deleteTestTable"}}
36 | DELETE FROM `TEST_TABLE`
37 | {{where (ne .Id 0) "AND" "\"id\"=" .Id "" | where (ne .UserName "") "AND" "\"username\"=" .UserName | where (ne .Password "pw") "AND" "\"password\"=" .Password | where (ne .Status -1) "AND" "\"status\"=" .Status }}
38 | {{end}}
39 |
40 | {{template "selectTestTable"}}
41 | {{template "insertTestTable"}}
42 | {{template "updateTestTable"}}
43 | {{template "deleteTestTable"}}
--------------------------------------------------------------------------------
/test/template/temp_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | tmp2 "github.com/acmestack/gobatis/parsing/template"
23 | "os"
24 | "testing"
25 | "text/template"
26 | "time"
27 | )
28 |
29 | type TestTable struct {
30 | Id int
31 | UserName string
32 | Password string
33 | Status int
34 | Time time.Time
35 | }
36 |
37 | var driverName = "mysql"
38 |
39 | func TestTemplate(t *testing.T) {
40 | tpl, err := template.ParseFiles("./sql.tpl")
41 | if err != nil {
42 | t.Fatal(err)
43 | }
44 |
45 | s := tpl.Templates()
46 | for _, v := range s {
47 | t.Log(v.Name())
48 | }
49 |
50 | var param = TestTable{Id: 1, UserName: "user", Password: "pw"}
51 | t.Run("select", func(t *testing.T) {
52 | tpl = tpl.Lookup("selectTestTable")
53 | if tpl == nil {
54 | t.Fatal("not found")
55 | }
56 |
57 | err = tpl.Execute(os.Stdout, param)
58 | if err != nil {
59 | t.Fatal(err)
60 | }
61 | })
62 |
63 | t.Run("insert", func(t *testing.T) {
64 | tpl = tpl.Lookup("insertTestTable")
65 | if tpl == nil {
66 | t.Fatal("not found")
67 | }
68 |
69 | err = tpl.Execute(os.Stdout, param)
70 | if err != nil {
71 | t.Fatal(err)
72 | }
73 | })
74 |
75 | t.Run("update", func(t *testing.T) {
76 | tpl = tpl.Lookup("updateTestTable")
77 | if tpl == nil {
78 | t.Fatal("not found")
79 | }
80 |
81 | err = tpl.Execute(os.Stdout, param)
82 | if err != nil {
83 | t.Fatal(err)
84 | }
85 | })
86 |
87 | t.Run("delete", func(t *testing.T) {
88 | tpl = tpl.Lookup("deleteTestTable")
89 | if tpl == nil {
90 | t.Fatal("not found")
91 | }
92 |
93 | err = tpl.Execute(os.Stdout, param)
94 | if err != nil {
95 | t.Fatal(err)
96 | }
97 | })
98 | }
99 |
100 | func TestParser(t *testing.T) {
101 | mgr := tmp2.NewManager()
102 | mgr.RegisterFile("./sql.tpl")
103 | var param = TestTable{Id: 1, UserName: "user", Password: "pw", Time: time.Now()}
104 |
105 | t.Run("select", func(t *testing.T) {
106 | tmp, _ := mgr.FindSqlParser("selectTestTable")
107 | md, err := tmp.ParseMetadata(driverName, param)
108 | if err != nil {
109 | t.Fatal(err)
110 | }
111 | t.Log(md)
112 | })
113 |
114 | t.Run("insert", func(t *testing.T) {
115 | tmp, _ := mgr.FindSqlParser("insertTestTable")
116 | md, err := tmp.ParseMetadata(driverName, param)
117 | if err != nil {
118 | t.Fatal(err)
119 | }
120 | t.Log(md)
121 | })
122 |
123 | t.Run("insertBatch", func(t *testing.T) {
124 | tmp, _ := mgr.FindSqlParser("insertBatchTestTable")
125 | md, err := tmp.ParseMetadata(driverName, []TestTable{
126 | {Id: 1, UserName: "user1", Password: "pw1"},
127 | {Id: 2, UserName: "user2", Password: "pw2"},
128 | })
129 | if err != nil {
130 | t.Fatal(err)
131 | }
132 | t.Log(md)
133 | })
134 |
135 | t.Run("update", func(t *testing.T) {
136 | tmp, _ := mgr.FindSqlParser("updateTestTable")
137 | md, err := tmp.ParseMetadata(driverName, param)
138 | if err != nil {
139 | t.Fatal(err)
140 | }
141 | t.Log(md)
142 | })
143 |
144 | t.Run("delete", func(t *testing.T) {
145 | tmp, _ := mgr.FindSqlParser("deleteTestTable")
146 | md, err := tmp.ParseMetadata(driverName, param)
147 | if err != nil {
148 | t.Fatal(err)
149 | }
150 | t.Log(md)
151 | })
152 | }
153 |
154 | func TestParser2(t *testing.T) {
155 | gobatis.RegisterTemplateFile("./sql.tpl")
156 | t.Run("select", func(t *testing.T) {
157 | p, ok := gobatis.FindTemplateSqlParser("selectTestTable")
158 | if !ok {
159 | t.Fatal(ok)
160 | }
161 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
162 | if err != nil {
163 | t.Fatal(err)
164 | }
165 |
166 | t.Log(md)
167 | })
168 |
169 | t.Run("insert", func(t *testing.T) {
170 | p, ok := gobatis.FindTemplateSqlParser("insertTestTable")
171 | if !ok {
172 | t.Fatal(ok)
173 | }
174 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
175 | if err != nil {
176 | t.Fatal(err)
177 | }
178 |
179 | t.Log(md)
180 | })
181 |
182 | t.Run("insertBatch", func(t *testing.T) {
183 | p, ok := gobatis.FindTemplateSqlParser("insertBatchTestTable")
184 | if !ok {
185 | t.Fatal(ok)
186 | }
187 | md, err := p.ParseMetadata("mysql", []TestTable{
188 | {Id: 11, UserName: "user11", Password: "pw11"},
189 | {Id: 12, UserName: "user12", Password: "pw12"},
190 | })
191 | if err != nil {
192 | t.Fatal(err)
193 | }
194 |
195 | t.Log(md)
196 | })
197 |
198 | t.Run("update", func(t *testing.T) {
199 | p, ok := gobatis.FindTemplateSqlParser("updateTestTable")
200 | if !ok {
201 | t.Fatal(ok)
202 | }
203 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
204 | if err != nil {
205 | t.Fatal(err)
206 | }
207 |
208 | t.Log(md)
209 | })
210 |
211 | t.Run("delete", func(t *testing.T) {
212 | p, ok := gobatis.FindTemplateSqlParser("deleteTestTable")
213 | if !ok {
214 | t.Fatal(ok)
215 | }
216 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
217 | if err != nil {
218 | t.Fatal(err)
219 | }
220 |
221 | t.Log(md)
222 | })
223 | }
224 |
--------------------------------------------------------------------------------
/test/template/temp_v2_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package template
19 |
20 | import (
21 | "github.com/acmestack/gobatis"
22 | tmp2 "github.com/acmestack/gobatis/parsing/template"
23 | "os"
24 | "testing"
25 | "text/template"
26 | "time"
27 | )
28 |
29 | func TestTemplateV2(t *testing.T) {
30 | tmp2.SetDynamicFactory(tmp2.CreateV2DynamicHandler)
31 | tpl, err := template.ParseFiles("./sql_v2.tpl")
32 | if err != nil {
33 | t.Fatal(err)
34 | }
35 |
36 | s := tpl.Templates()
37 | for _, v := range s {
38 | t.Log(v.Name())
39 | }
40 |
41 | var param = TestTable{Id: 1, UserName: "user", Password: "pw"}
42 | t.Run("select", func(t *testing.T) {
43 | tpl = tpl.Lookup("selectTestTable")
44 | if tpl == nil {
45 | t.Fatal("not found")
46 | }
47 |
48 | err = tpl.Execute(os.Stdout, param)
49 | if err != nil {
50 | t.Fatal(err)
51 | }
52 | })
53 |
54 | t.Run("insert", func(t *testing.T) {
55 | tpl = tpl.Lookup("insertTestTable")
56 | if tpl == nil {
57 | t.Fatal("not found")
58 | }
59 |
60 | err = tpl.Execute(os.Stdout, param)
61 | if err != nil {
62 | t.Fatal(err)
63 | }
64 | })
65 |
66 | t.Run("update", func(t *testing.T) {
67 | tpl = tpl.Lookup("updateTestTable")
68 | if tpl == nil {
69 | t.Fatal("not found")
70 | }
71 |
72 | err = tpl.Execute(os.Stdout, param)
73 | if err != nil {
74 | t.Fatal(err)
75 | }
76 | })
77 |
78 | t.Run("delete", func(t *testing.T) {
79 | tpl = tpl.Lookup("deleteTestTable")
80 | if tpl == nil {
81 | t.Fatal("not found")
82 | }
83 |
84 | err = tpl.Execute(os.Stdout, param)
85 | if err != nil {
86 | t.Fatal(err)
87 | }
88 | })
89 | }
90 |
91 | func TestParserV2(t *testing.T) {
92 | mgr := tmp2.NewManager()
93 | tmp2.SetDynamicFactory(tmp2.CreateV2DynamicHandler)
94 | mgr.RegisterFile("./sql_v2.tpl")
95 | var param = TestTable{Id: 1, UserName: "user", Password: "pw", Time: time.Now()}
96 |
97 | t.Run("select", func(t *testing.T) {
98 | tmp, _ := mgr.FindSqlParser("selectTestTable")
99 | md, err := tmp.ParseMetadata(driverName, param)
100 | if err != nil {
101 | t.Fatal(err)
102 | }
103 | t.Log(md)
104 | })
105 |
106 | t.Run("insert", func(t *testing.T) {
107 | tmp, _ := mgr.FindSqlParser("insertTestTable")
108 | md, err := tmp.ParseMetadata(driverName, param)
109 | if err != nil {
110 | t.Fatal(err)
111 | }
112 | t.Log(md)
113 | })
114 |
115 | t.Run("insertBatch", func(t *testing.T) {
116 | tmp, _ := mgr.FindSqlParser("insertBatchTestTable")
117 | md, err := tmp.ParseMetadata(driverName, []TestTable{
118 | {Id: 1, UserName: "user1", Password: "pw1"},
119 | {Id: 2, UserName: "user2", Password: "pw2"},
120 | })
121 | if err != nil {
122 | t.Fatal(err)
123 | }
124 | t.Log(md)
125 | })
126 |
127 | t.Run("update", func(t *testing.T) {
128 | tmp, _ := mgr.FindSqlParser("updateTestTable")
129 | md, err := tmp.ParseMetadata(driverName, param)
130 | if err != nil {
131 | t.Fatal(err)
132 | }
133 | t.Log(md)
134 | })
135 |
136 | t.Run("delete", func(t *testing.T) {
137 | tmp, _ := mgr.FindSqlParser("deleteTestTable")
138 | md, err := tmp.ParseMetadata(driverName, param)
139 | if err != nil {
140 | t.Fatal(err)
141 | }
142 | t.Log(md)
143 | })
144 | }
145 |
146 | func TestParser2V2(t *testing.T) {
147 | tmp2.SetDynamicFactory(tmp2.CreateV2DynamicHandler)
148 | gobatis.RegisterTemplateFile("./sql_v2.tpl")
149 | t.Run("select", func(t *testing.T) {
150 | p, ok := gobatis.FindTemplateSqlParser("selectTestTable")
151 | if !ok {
152 | t.Fatal(ok)
153 | }
154 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
155 | if err != nil {
156 | t.Fatal(err)
157 | }
158 |
159 | t.Log(md)
160 | })
161 |
162 | t.Run("insert", func(t *testing.T) {
163 | p, ok := gobatis.FindTemplateSqlParser("insertTestTable")
164 | if !ok {
165 | t.Fatal(ok)
166 | }
167 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
168 | if err != nil {
169 | t.Fatal(err)
170 | }
171 |
172 | t.Log(md)
173 | })
174 |
175 | t.Run("insertBatch", func(t *testing.T) {
176 | p, ok := gobatis.FindTemplateSqlParser("insertBatchTestTable")
177 | if !ok {
178 | t.Fatal(ok)
179 | }
180 | md, err := p.ParseMetadata("mysql", []TestTable{
181 | {Id: 11, UserName: "user11", Password: "pw11"},
182 | {Id: 12, UserName: "user12", Password: "pw12"},
183 | })
184 | if err != nil {
185 | t.Fatal(err)
186 | }
187 |
188 | t.Log(md)
189 | })
190 |
191 | t.Run("update", func(t *testing.T) {
192 | p, ok := gobatis.FindTemplateSqlParser("updateTestTable")
193 | if !ok {
194 | t.Fatal(ok)
195 | }
196 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
197 | if err != nil {
198 | t.Fatal(err)
199 | }
200 |
201 | t.Log(md)
202 | })
203 |
204 | t.Run("delete", func(t *testing.T) {
205 | p, ok := gobatis.FindTemplateSqlParser("deleteTestTable")
206 | if !ok {
207 | t.Fatal(ok)
208 | }
209 | md, err := p.ParseMetadata("mysql", TestTable{Id: 1, UserName: "user", Password: "pw", Status: 10})
210 | if err != nil {
211 | t.Fatal(err)
212 | }
213 |
214 | t.Log(md)
215 | })
216 | }
217 |
--------------------------------------------------------------------------------
/test/xml_file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package test
19 |
20 | var test_xml = `
21 |
22 |
36 |
37 |
48 |
49 |
60 |
61 |
70 | insert into Author (id,username,password,email,bio)
71 | values (#{id},#{username},#{password},#{email},#{bio})
72 |
73 |
74 |
75 | id, username, password
76 |
77 |
78 |
84 | update Author set
85 | username = #{username},
86 | password = #{password},
87 | email = #{email},
88 | bio = #{bio}
89 | where id = #{id}
90 |
91 |
92 |
98 | delete from Author where id = #{id}
99 |
100 |
101 |
102 | /*通过${}取调用者传递的属性值*/
103 | from ${tableName}
104 |
105 |
106 |
107 | `
108 |
--------------------------------------------------------------------------------
/transaction/default_transaction.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package transaction
19 |
20 | import (
21 | "context"
22 | "database/sql"
23 |
24 | "github.com/acmestack/gobatis/common"
25 | "github.com/acmestack/gobatis/connection"
26 | "github.com/acmestack/gobatis/datasource"
27 | "github.com/acmestack/gobatis/errors"
28 | "github.com/acmestack/gobatis/reflection"
29 | "github.com/acmestack/gobatis/statement"
30 | "github.com/acmestack/gobatis/util"
31 | )
32 |
33 | type DefaultTransaction struct {
34 | ds datasource.DataSource
35 | db *sql.DB
36 | tx *sql.Tx
37 | }
38 |
39 | func NewDefaultTransaction(ds datasource.DataSource, db *sql.DB) *DefaultTransaction {
40 | ret := &DefaultTransaction{ds: ds, db: db}
41 | return ret
42 | }
43 |
44 | func (trans *DefaultTransaction) GetConnection() connection.Connection {
45 | if trans.tx == nil {
46 | return (*connection.DefaultConnection)(trans.db)
47 | } else {
48 | return &TransactionConnection{tx: trans.tx}
49 | }
50 | }
51 |
52 | func (trans *DefaultTransaction) Close() {
53 |
54 | }
55 |
56 | func (trans *DefaultTransaction) Begin() error {
57 | tx, err := trans.db.Begin()
58 | if err != nil {
59 | return err
60 | }
61 | trans.tx = tx
62 | return nil
63 | }
64 |
65 | func (trans *DefaultTransaction) Commit() error {
66 | if trans.tx == nil {
67 | return errors.TransactionWithoutBegin
68 | }
69 |
70 | err := trans.tx.Commit()
71 | if err != nil {
72 | return errors.TransactionCommitError
73 | }
74 | return nil
75 | }
76 |
77 | func (trans *DefaultTransaction) Rollback() error {
78 | if trans.tx == nil {
79 | return errors.TransactionWithoutBegin
80 | }
81 |
82 | err := trans.tx.Rollback()
83 | if err != nil {
84 | return errors.TransactionCommitError
85 | }
86 | return nil
87 | }
88 |
89 | type TransactionConnection struct {
90 | tx *sql.Tx
91 | }
92 |
93 | type TransactionStatement struct {
94 | tx *sql.Tx
95 | sql string
96 | }
97 |
98 | func (transConnection *TransactionConnection) Prepare(sqlStr string) (statement.Statement, error) {
99 | ret := &TransactionStatement{
100 | tx: transConnection.tx,
101 | sql: sqlStr,
102 | }
103 | return ret, nil
104 | }
105 |
106 | func (transConnection *TransactionConnection) Query(ctx context.Context, result reflection.Object, sqlStr string, params ...interface{}) error {
107 | db := transConnection.tx
108 | rows, err := db.QueryContext(ctx, sqlStr, params...)
109 | if err != nil {
110 | return errors.StatementQueryError
111 | }
112 | defer rows.Close()
113 |
114 | util.ScanRows(rows, result)
115 | return nil
116 | }
117 |
118 | func (transConnection *TransactionConnection) Exec(ctx context.Context, sqlStr string, params ...interface{}) (common.Result, error) {
119 | db := transConnection.tx
120 | return db.ExecContext(ctx, sqlStr, params...)
121 | }
122 |
123 | func (transStatement *TransactionStatement) Query(ctx context.Context, result reflection.Object, params ...interface{}) error {
124 | rows, err := transStatement.tx.QueryContext(ctx, transStatement.sql, params...)
125 | if err != nil {
126 | return errors.StatementQueryError
127 | }
128 | defer rows.Close()
129 |
130 | util.ScanRows(rows, result)
131 | return nil
132 | }
133 |
134 | func (transStatement *TransactionStatement) Exec(ctx context.Context, params ...interface{}) (common.Result, error) {
135 | return transStatement.tx.ExecContext(ctx, transStatement.sql, params...)
136 | }
137 |
138 | func (transStatement *TransactionStatement) Close() {
139 | //Will be closed when commit or rollback
140 | }
141 |
--------------------------------------------------------------------------------
/transaction/transaction.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package transaction
19 |
20 | import (
21 | "github.com/acmestack/gobatis/connection"
22 | )
23 |
24 | type Transaction interface {
25 | Close()
26 |
27 | GetConnection() connection.Connection
28 |
29 | Begin() error
30 |
31 | Commit() error
32 |
33 | Rollback() error
34 | }
35 |
--------------------------------------------------------------------------------
/util/rowutil.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the AcmeStack under one or more contributor license
3 | * agreements. See the NOTICE file distributed with this work for
4 | * additional information regarding copyright ownership.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package util
19 |
20 | import (
21 | "database/sql"
22 | "github.com/acmestack/gobatis/reflection"
23 | "reflect"
24 | )
25 |
26 | func ScanRows(rows *sql.Rows, result reflection.Object) int64 {
27 | columns, _ := rows.Columns()
28 |
29 | scanArgs := make([]interface{}, len(columns))
30 | values := make([]interface{}, len(columns))
31 |
32 | for j := range values {
33 | scanArgs[j] = &values[j]
34 | }
35 |
36 | var index int64 = 0
37 | for rows.Next() {
38 | if err := rows.Scan(scanArgs...); err == nil {
39 | //for _, col := range values {
40 | // logging.Debug("%v", col)
41 | //}
42 | if !deserialize(result, columns, values) {
43 | break
44 | }
45 | index++
46 | }
47 | }
48 | return index
49 | }
50 |
51 | func deserialize(result reflection.Object, columns []string, values []interface{}) bool {
52 | obj := result
53 | if result.CanAddValue() {
54 | obj = result.NewElem()
55 | }
56 | for i := range columns {
57 | if obj.CanSetField() {
58 | obj.SetField(columns[i], values[i])
59 | } else {
60 | obj.SetValue(reflect.ValueOf(values[0]))
61 | break
62 | }
63 | }
64 | if result.CanAddValue() {
65 | result.AddValue(obj.GetValue())
66 | return true
67 | }
68 | return false
69 | }
70 |
--------------------------------------------------------------------------------