├── .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 | --------------------------------------------------------------------------------