set和map增删查改的使用

本文详细介绍了C++ STL中的set和map的使用,包括它们的内部实现(红黑树)、增删查改操作。set是存储唯一元素且自动排序的容器,不允许元素值重复;map是存储键值对并按键排序的容器,键必须唯一。文章还提到了multiset和multimap,允许元素或键的重复。示例代码展示了如何插入、查找、删除元素,以及通过operator[]修改map中的值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

set的内部实现是基于红黑树,set就是以红黑树的格式存储内部的所有元素的。
set中的所有元素都会根据元素的键值自动被排序。set的元素不像map那样可以同时拥有实值(value)和键值(key)。set中只有一个值,set不允许两个元素有相同的值。
我们不可以使用迭代器修改set的元素值,元素值已经按照红黑树的方式排列好,如果修改就会改变其组织。set中,可以允许删除和增加一些元素。

map的内部实现同样是基于红黑树。
和set不同,map的所有元素都是一个键值对pair(key,value),所有元素都会按照key的值进行排序,map中不允许有相同的key值,但可以修改map中元素的value值。
而multiset和multimap的特性和set和map一样,唯一的差别是允许key重复
1 set和multiset
set和multiset会根据特定的排序准则,自动将元素进行排序。不同的是后者允许元素重复而前者不允许。
set和multiset两种头文件都为:#include<set>
set和multiset都是定义在std空间里的类模板。

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;
template < class T,                        // multiset::key_type/value_type
           class Compare = less<T>,        // multiset::key_compare/value_compare
           class Alloc = allocator<T> >    // multiset::allocator_type
           > class multiset;

其中第二个参数来定义排序准则为仿函数,默认为less,也就意味着,默认遍历结果为升序。例:

    set<int,greater<int>> s1;
    s1.insert(10);
    s1.insert(3);
    s1.insert(9);
    s1.insert(8);
    s1.insert(1);
    s1.insert(3);

    set<int,greater<int>>::iterator it1 = s1.begin();
    while (it1 != s1.end())
    {
        cout << *it1 << "  ";
        ++it1;
    }
    cout << endl;

运行结果:
这里写图片描述
相关操作
这里写图片描述这里写图片描述
1.1 插入
插入用insert函数

pair<iterator,bool> insert (const value_type& val);//pair::first set to an iterator pointing to either the newly inserted element or to the equivalent element already in the set. The pair::second element in the pair is set to true if a new element was inserted or false if an equivalent element already existed.
iterator insert (iterator position, const value_type& val);//在指定的位置插入指定的数据,position是一个迭代器的位置,x表示的是要插入的数。如果插入成功的话,会返回一个插入新位置的迭代器。
template <class InputIterator>
void insert (InputIterator first, InputIterator last);//插入一个迭代器区间

pair是一种模板模型,每个pair可以存储两个值,这俩个值得类型是任意类型。定义在#include中,其标准形式为:template< class T1,class T2> struct pair;
pair有两个成员分别是first和second,set是一个模板,first是一个指向要插入位置的迭代器,如果second是false则first就是已经存在的元素的这个位置的迭代器,如果second为true,first就是要插入位置的迭代器。
结构如下:

template<class K,class V>
class pair
{
public:
    pair(const K& key, const V& v)
        :first(key)
        , second(v)
    {   }
    const K first;
    V second;
};

例:

set<int> myset;
set<int>::iterator it;
pair<iterator,bool> ret;
// 初始化
for (int i=1; i<=5; ++i) myset.insert(i*10);  
ret = myset.insert(20);  //20存在,插入失败 
if (ret.second==false) it=ret.first;  // it指向20
myset.insert (it,25);    
myset.insert (it,24);          
myset.insert (it,26);       
int myints[]= {5,10,15};   
myset.insert (myints,myints+3);
for (it=myset.begin(); it!=myset.end(); ++it)
      cout << ' ' << *it;
cout << '\n';

1.2 查找
查找用find()函数

iterator find(const key_type& x) const;//若找到返回该迭代器的位置,否则就返回的是最后一个元素后面的位置

使用count()函数

size_type count(const key_type& x)const;//count是为了统计某个数值出现的次数,在set里面不能重复出现相同的数值,count的返回值只有01两个值,0表示的是不存在,1表示存在。

例:

set<int> s3;
s3.insert(10);
s3.insert(3);
s3.insert(9);
s3.insert(8);
s3.insert(1);
s3.insert(3);
set<int>::iterator pos = s3.find(10);//当迭代器没找到返回end,end是最后一个数据的下一个
if (s3.count(8))
{
    cout << "exit" << endl;
}
else
{
    cout << "Non" << endl;
}

1.3 删除

void erase(iterator position)//删除一个迭代器位置
size_type erase(sonst key_type& x)//删除成功的话就返回1
void erase(iterator first, iterator last)//删除一段迭代器区间

例:删除一个迭代器位置

set<int> s3;
s3.insert(10);
s3.insert(3);
s3.insert(9);
s3.insert(8);
s3.insert(1);
s3.insert(3);
set<int>::iterator pos = s3.find(10);//当迭代器没找到返回end,end是最后一个数据的下一个
if (pos!=s3.end())
    s3.erase(pos);

例:删除一段迭代器区间

set<int> s4;
set<int>::iterator itlow, itup;
for (int i = 1; i<10; i++) s4.insert(i * 10); // 10 20 30 40 50 60 70 80 90
itlow = s4.lower_bound(35);          
itup = s4.upper_bound(65);
s4.erase(itlow, itup);
set<int>::iterator it4 = s4.begin();
while (it4 != s4.end())
{
    cout << *it4 << "  ";
    ++it4;
}
cout << endl;

1.4 multiset在set的基础上允许键值冗余
例:

multiset<int> s4;
s4.insert(10);
s4.insert(3);
s4.insert(9);
s4.insert(8);
s4.insert(1);
s4.insert(3);
multiset<int>::iterator it4 = s4.begin();
while (it4 != s4.end())
{
    cout << *it4 << "  ";
    ++it4;
}
cout << endl;
multiset<int>::iterator pos = s4.find(3);
++pos;
cout << *pos << endl;
if (pos != s4.end())
    s4.erase(pos);
it4 = s4.begin();
while (it4 != s4.end())
{
    cout << *it4 << "  ";
    ++it4;
}
cout << endl;

运行结果:
这里写图片描述
2 map和multimap
map和multimap两种头文件都为:#include<map>
map是一种key(键),value(值)的形式,用来保存键和值组成的集合,键必须是唯一的,但值可以不唯一。里面的元素可以根据键进行自动排序,由于map是key_value的形式,所以map里的所有元素都是pair类型。pair里面的first被称为key(键),second被称为value(值)。map的底层是用红黑树来实现的。
在刚开始介绍set的时候,简单介绍了一下pair。现在将pair和make_pair再做一个详细的补充吧
1)pair
pair是将两个数据合成一个数据,当有这样的需求时就要用到pair,就是map。还有一个运用就是函数要返回连个数据的时候要用到pair,pair是一个结构体,因为两个成员变量用的是struct而不是class。
2)make_pair
可以使用pair的构造函数,也可以使用make_ pair来生成我们需要的pair。一般来说,在需要pair的时候我们用make_pair来生成pair的对象很方便,但由于pair可以进行隐式的类型转换局带来一些问题。

template<class K,class V>
inline pair<K, V> make_pair(const K& k,const V& v)
{
    return pair<K, V>(k, v);
}

相关操作
这里写图片描述这里写图片描述
例:

map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));
dict.insert(pair<string, string>("insert", "插入"));
dict.insert(pair<string, string>("dict", "字典"));
map<string, string>::iterator it1 = dict.begin();
while (it1 != dict.end())
{
    //cout << (*it1).first << ":" << (*it1).second << endl;
    cout << it1->first << ":" << it1->second << endl;
    ++it1;
}
map<string, string>::iterator pos= dict.find("insert");
(*pos).second = "XXXXXXXXXXXXX";

2.1 插入insert()

pair<iterator,bool> insert (const value_type& val);//插入一个value_type类型,返回的则是一个pair类型。pair<iterator,bool>就是pair<map<K,V>::iterator,bool>。如果插入成功的话bool值就返回的是true,iterator指向插入的位置,否则的话iterator就指向已经存在的这个元素的位置,iterator是一个pair<K,V>类型的迭代器。
iterator insert (iterator position, const value_type& val);//在一个迭代器的位置插入一个value_type类型,插入成功的话就返回新插入位置的迭代器,否则就返回传入的迭代器。
template <class InputIterator>
void insert (InputIterator first, InputIterator last);//插入的是一段迭代器区间

例:

map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));    dict.insert(pair<string, string>("insert", "插入"));
dict.insert(pair<string, string>("dict", "字典"));
map<string, string>::iterator it1 = dict.begin();
while (it1 != dict.end())
{
    //cout << (*it1).first << ":" << (*it1).second << endl;
    cout << it1->first << ":" << it1->second << endl;
    ++it1;
}

2.2 查找
find()函数

//查找成功的话会返回这个元素的迭代器,否则返回的是end
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;

例:

map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));
dict.insert(pair<string, string>("insert", "插入"));
dict.insert(pair<string, string>("dict", "字典"));
map<string, string>::iterator pos= dict.find("insert");
(*pos).second = "XXXXXXXXXXXXX";

count()函数

size_type count ( const key_type& x ) const;

和set中的count使用功能相同,count的返回值只有0和1两个值,0表示的是不存在,1表示存在。
2.3 删除

void erase ( iterator position );//删除的是position位置的元素
size_type erase ( const key_type& x );//删除的是键值所在的位置,成功为1,否则为0
void erase ( iterator first, iterator last );//删除一段迭代器区间

例:

map<string, string>::iterator pos= dict.find("insert");
if (pos != dict.end())
{
    dict.erase(pos);
}

2.4 operator[]
可以实现修改和插入

mapped_type& operator[] (const key_type& k);

这里写图片描述
operator[]是很常用的,如果map中有这个key,则它就把这个key所对应的value的引用返回。如果map中没有这个key的话,则它会调用insert(pair< K ,V > (k,V())),将k和V的缺省值对应起来插入后并返回这个value的引用。
其实现如下:

mapped_type& operator[] (const key_type& k)
{
     return (*((this->insert(make_pair(k,mapped_type()))).first)).second;
}

例:

string strs[] = { "sort", "sort", "sort", "insert", "insert","second", "left" };
map<string, size_t> countMap; 
for (size_t i = 0; i < sizeof(strs) / sizeof(string); ++i)
{
    //方法1
    map<string, size_t>::iterator it = countMap.find(strs[i]);
    if (it != countMap.end())
    {
        it->second++;
    }
    else
    {
        //countMap.insert(pair<string, size_t>(strs[i], 1));
        countMap.insert(make_pair(strs[i], 1));
    }
    //方法2
    pair<map<string, size_t>::iterator, bool> ret = countMap.insert(make_pair(strs[i], 1));
    if (ret.second == false)
    {
        ret.first->second++;
    }
    //方法3
    countMap[strs[i]]++;//[]的返回值为value的引用
}
map<string, size_t>::iterator it1 = countMap.begin();
while (it1 != countMap.end())
{
    cout << it1->first << ":" << it1->second << endl;
    it1++;
}   
cout << endl;

运行结果:
这里写图片描述
例:

map<string, string> dict;
dict["sort"] = "排序";//用[],其中value必须要有缺省的构造函数
dict["insert"] = "字符串";
dict["left"];
dict["left"]="剩余";
dict["left"] = "左边";
dict.insert(make_pair("first", "XXXXXX"));
dict.insert(make_pair("first", "YYYYYYYYYY"));
map<string, string>::iterator it1 = dict.begin();
while (it1 != dict.end())
{
    cout << it1->first << ":" << it1->second << endl;
    it1++;
}   
cout << endl;

这里写图片描述

3 map和set模拟实现代码
map和set的底层为红黑树,故而要借助红黑树

//RBtree.h
#pragma once 
#include<utility>//包含pair类型
enum Colour
{
    RED,
    BLACK,
};

template<class V>
struct RBTreeNode
{
    RBTreeNode<V>* _left;
    RBTreeNode<V>* _right;
    RBTreeNode<V>* _parent;

    V _value;
    Colour _col;
    RBTreeNode(const V& value = V())
        :_value(value)
        , _left(NULL)
        , _right(NULL)
        , _parent(NULL)
        , _col(RED)//初始化插入节点的颜色为红色,不影响黑节点个数 
    {}
};

//迭代器
template<class V, class Ref, class Ptr>
struct _RBTreeIterator
{
    typedef RBTreeNode<V> Node;
    typedef _RBTreeIterator<V, Ref, Ptr> Self;
    Node* _node;

    _RBTreeIterator()
    {}
    _RBTreeIterator(Node* node)
        :_node(node)
    {}

    Ref operator *()
    {
        return _node->_value;
    }
    Ptr operator ->()
    {
        return &(operator *());
    }
    Self operator ++()
    {
        if (_node->_right)
        {
            Node* subleft = _node->_right;
            while (subleft->_left)
            {
                subleft = subleft->_left;
            }
            _node = subleft;
        }
        else
        {
            Node* Parent = _node->_parent;
            Node* cur = _node;
            while (Parent&&cur == Parent->_right)
            {
                cur = Parent;
                Parent = Parent->_parent;
            }
            _node = Parent;
        }
        return *this;
    }

    Self operator ++(int)
    {
        Self it(*this);
        ++(*this);
        return it;
    }
    Self operator --()
    {
        if (_node->_left)
        {
            Node* subright = _node->_left;
            while (subright->_right)
            {
                subright = subright->_right;
            }
            _node = subright;
        }
        else
        {
            Node* Parent = _node->_parent;
            Node* cur = _node;
            while(Parent&&cur == Parent->_left)
            {
                cur = Parent;
                Parent = Parent->_parent;
            }
            _node = Parent;
        }
        return *this;
    }
    Self operator --(int)
    {
        Self it(*this);
        --(*this);
        return *this;
    }
    bool operator !=(const Self& s)
    {
        return _node != s._node;
    }

};

//my_set->rbtree<K,K>
//my_map->rbtree<K,pair<K,V>)

//template<class K, class V>
//class pair
//{
//public:
//  pair(const K& key, const V& v)
//      :first(key)
//      ,second(v)
//  {}
//
//  const K first;
//  V second;
//};
//
//template<class K, class V>
//inline pair<K, V> make_pair(const K& k, const V& v)
//{
//  return pair<K, V>(k, v);
//}

template<class K, class V, class KeyOfValue>
struct RBTree
{
    typedef RBTreeNode<V> Node;
public:
    typedef _RBTreeIterator<V, V&, V*> Iterator;
    typedef _RBTreeIterator<V, const V&, const V*> ConstIterator;
    RBTree()
        :_root(NULL)
    {}
    Iterator Begin()
    {
        Node* left = _root;
        while (left->_left)
        {
            left = left->_left;
        }
        return left;
    }
    Iterator End()
    {
        return NULL;
    }
    std::pair<Iterator,bool> Insert(const V& value)
    {
        if (_root == NULL)
        {
            _root = new Node(value);
            _root->_col = BLACK;
            return make_pair(Iterator(_root),true);
        }
        KeyOfValue kofv;
        Node* parent = NULL;
        Node* cur = _root;
        while (cur)
        {
            if (kofv(cur->_value) > kofv(value))//三叉链
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (kofv(cur->_value) < kofv(value))
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return make_pair(Iterator(cur),false);
            }
        }
        cur = new Node(value);
        Node* ret = cur;
        if (kofv(parent->_value) < kofv(value))
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        // 调平衡 
        //检查规则:1.parent为黑,不用调整
        //2.parent为红,uncle为红
        //3.parent为红,uncle为黑或者不存在
        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                if (uncle&&uncle->_col == RED)//parent为红,uncle为红
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//parent为红,uncle为黑或者不存在,分单旋和双旋两种情况
                {
                    if (cur == parent->_right)//双旋
                    {
                        RotateL(parent);
                        swap(cur, parent);
                    }
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
            }
            else
            {
                Node* uncle = grandfather->_left;
                if (uncle&&uncle->_col == RED)//parent为红,uncle为红
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//parent为红,uncle为黑或者不存在,分单旋和双旋两种情况
                {
                    if (cur == parent->_left)//双旋
                    {
                        RotateR(parent);
                        swap(cur, parent);
                    }
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
            }
        }
        _root->_col = BLACK;
        return make_pair(Iterator(ret),true);
    }

    void RotateR(Node* parent)//右旋
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR)
        {
            subLR->_parent = parent;
        }
        Node* PPnode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        if (parent == _root)
        {
            _root = subL;
        }
        else
        {
            if (PPnode->_left == parent)
            {
                PPnode->_left = subL;
            }
            else
            {
                PPnode->_right = subL;
            }
            subL->_parent = PPnode;
        }
        _root->_parent = NULL;
    }


    void RotateL(Node* parent)//左旋
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        parent->_right = subRL;
        if (subRL)
        {
            subRL->_parent = parent;
        }
        Node* PPnode = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;
        if (parent == _root)
        {
            _root = subR;
            _root->_parent = NULL;
        }
        else
        {
            if (PPnode->_left == parent)
            {
                PPnode->_left = subR;
                subR->_parent = PPnode;
            }
            else
            {
                PPnode->_right = subR;
                subR->_parent = PPnode;
            }
        }
    }


    bool _IsBalance(Node* root, const int & count, int num)
    {
        if (root->_col == NULL)
            return true;
        if (root->_col == RED&&root->_parent->_col == RED)//存在两个连续的红节点 
            return false;
        if (root->_col == BLACK)//黑节点就CurBlackNum++  
            num++;
        if (root->_left == NULL&&root->_right == NULL)
        {
            if (num == count)
            {
                return true;
            }
            else
            {
                return false;//路径上黑节点个数不同返回false
            }
        }
        return _IsBalance(root->_left, count, num) && _IsBalance(root->_right, count, num);
    }
    bool IsBalance()//是否为红黑树
    {
        if (_root->_col != BLACK)
        {
            return false;
        }
        int count = 0;//统计出一条路径的黑色节点的个数  
        int num = 0;//需要与count比较的其他路径黑色节点个数 
        Node* cur = _root;
        while (cur)
        {
            if (cur->_col == BLACK)
            {
                count++;
            }
            cur = cur->_right;
        }
        return _IsBalance(_root, count, num);
    }

    void _InOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }
        _InOrder(root->_left);
        //  cout << kofv(root->_value) << "  ";
        _InOrder(root->_right);
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    Iterator Find(const K& key)
    {
        KeyOfValue kofv;
        Node* cur = _root;
        while (cur)
        {
            if (kofv(cur->_value) > key)//三叉链
            {
                cur = cur->_left;
            }
            else if (kofv(cur->_value) < key)
            {
                cur = cur->_right;
            }
            else
            {
                return Iterator(cur);
            }
        }
        return End();
    }

private:
    Node* _root;
};


struct SetKeyOfValue
{
    const int& operator()(const int& key)
    {
        return key;
    }
};
//Myset.h
#pragma once
#include"RBtree.h"
#include<utility>//包含pair类型
#include<string>

template<typename K>
class Set
{
public:
    struct SetKeyOfValue
    {
        const K& operator()(const K& key)
        {
            return key;
        }
    };
    typedef typename RBTree<K, K, SetKeyOfValue>::Iterator Iterator;
    std::pair<Iterator, bool> Insert(const K& key)
    {
        return _t.Insert(key);
    }
    Iterator Find(const K& key)
    {
        return _t.Find(key);
    }
    Iterator Begin()
    {
        return _t.Begin();
    }
    Iterator End()
    {
        return _t.End();
    }

protected:
    RBTree<K, K, SetKeyOfValue> _t;
};
//Mymap.h
#pragma once
#include<utility>//包含pair类型
#include"RBtree.h"

#include<string>

template<class K, class V>
class Map
{
public:
    struct MapKeyOfValue
    {
        const K& operator()(const std::pair<K, V>& kv)
        {
            return kv.first;
        }
    };
    typedef typename RBTree<K, std::pair<K, V>, MapKeyOfValue>::Iterator Iterator;
    std::pair<Iterator, bool> Insert(std::pair<K, V>& kv)
    {
        return _t.Insert(kv);
    }
    V& operator [](const K& key)
    {
        pair<Iterator, bool> ret = Insert(make_pair(key, V()));
        return (ret.first)->second;
    }
    Iterator Find(const K& key)
    {
        return _t.Find(key);
    }
    Iterator Begin()
    {
        return _t.Begin();
    }
    Iterator End()
    {
        return _t.End();
    }
protected:

    RBTree<K, std::pair<K, V>, MapKeyOfValue> _t;

};
//test.cpp
#include<utility>//包含pair类型
#include"RBtree.h"
#include"Mymap.h"
#include"Myset.h"
#include<iostream>
#include"Windows.h"

using namespace std;

void TestRBTree()
{
    int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    RBTree<int, int, SetKeyOfValue> t;
    for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
    {
        t.Insert(a[i]);
    }
    RBTree<int, int, SetKeyOfValue>::Iterator it = t.Begin();
    while (it != t.End())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;
    it = t.Begin();
    ++it;
    ++it;
    ++it;
    --it;
    --it;
    cout << *it << endl;
}

void TestMySet()
{
    Set<string> s;
    s.Insert("sort");
    s.Insert("insert");
    s.Insert("set");
    s.Insert("map");
    s.Insert("iterator");
    s.Insert("value");
    s.Insert("value");
    Set<string>::Iterator it1 = s.Begin();
    while (it1 != s.End())
    {
        cout << *it1 << " ";
        ++it1;
    }
    cout << endl;
}
void TestMyMap()
{
    Map<string, string> dict;
    dict.Insert(pair<string, string>("left", "左边"));
    dict.Insert(pair<string, string>("dict", "字典"));
    dict.Insert(pair<string, string>("Insert", "字符串"));
    dict.Insert(pair<string, string>("map", "映射"));
    dict.Insert(pair<string, string>("left", "剩余"));
    dict["left"] = "剩余";
    dict["set"] = "集合";

    Map<string, string>::Iterator it = dict.Begin();
    while (it != dict.End())
    {
        cout << it->first << ":" << it->second << endl;
        ++it;
    }
    cout << endl;
}
int main()
{
    TestRBTree();
    TestMySet();
    TestMyMap();
    system("pause");
    return 0;
}

运行结果如下:
这里写图片描述

<think>嗯,用户之前问过如何使用JdbcTemplate进行查询,特别是将结果映射到User POJO。现在他们接着要求“写一下增删查改”,也就是CRUD操作。我需要确保覆盖所有四个操作:增(Create)、删(Delete)、查(Read)、改(Update)。之前已经详细讲了查询,所以这次重点在其他三个操作。 首先,用户可能已经掌握了查询部分,现在需要完整的CRUD示例,以便全面了解JdbcTemplate的使用。他们的身份可能是一个刚学习Spring JDBC的开发者,或者是在项目中需要实现数据库操作的初级程序员。需要确保示例清晰,步骤详细,特别是参数设置SQL语句的正确性。 接下来要考虑的是,用户可能有POJO类,但需要知道如何将对象属性映射到SQL参数。比如插入操作,可能需要使用PreparedStatement的Setter方法,或者使用JdbcTemplate的update方法传入参数数组。这里需要给出两种常见的方法:使用对象数组传递参数,或者使用PreparedStatementSetter来设置参数。 对于更新删除操作,用户需要注意SQL语句中的占位符(?)参数的顺序。此外,应该提醒异常处理,比如删除时可能出现的空结果,或者更新时的影响行数判断。同时,事务管理也是关键点,需要提到@Transactional注解的使用,以确保操作的原子性。 另外,用户可能对批量操作感兴趣,特别是插入多条数据时的效率问题。这时候需要给出批量更新的示例,比如使用batchUpdate方法,并传递参数列表。这对于处理大量数据时非常有用,能减少数据库访问次数,提升性能。 还需要注意代码示例的正确性,比如确保User类有无参构造函数,Getter/Setter方法是否齐全,以及数据库字段名与POJO属性名的匹配。在自定义RowMapper时,如果字段名不匹配,需要用别名或者显式映射。同时,异常情况的处理,比如queryForObject在结果为空时会抛出异常,需要捕获或者使用Optional处理。 最后,总结部分要强调最佳实践,比如使用BeanPropertyRowMapper的注意事项,性能优化,事务控制等。可能用户在实际应用中会遇到这些问题,提前给出提示可以帮助他们避免常见错误。 综上所述,回答需要结构清晰,分步骤介绍每个CRUD操作,提供代码示例,并补充注意事项最佳实践,确保用户能够全面理解并正确应用JdbcTemplate进行数据库操作。</think>以下是使用`org.springframework.jdbc.core.JdbcTemplate`实现 **增删查改(CRUD)** 的完整指南,以`User` POJO为例: --- ### 一、POJO定义(基础) ```java public class User { private Long id; private String username; private String email; // 必须有无参构造函数 public User() {} // Getter/Setter省略(实际需完整实现) } ``` --- ### 二、完整CRUD实现 #### 1. **新增(Create)** ```java public int addUser(User user) { String sql = "INSERT INTO users (username, email) VALUES (?, ?)"; return jdbcTemplate.update( sql, user.getUsername(), // 参数顺序与SQL中的?对应 user.getEmail() ); } // 或使用PreparedStatementSetter public int addUser(User user) { return jdbcTemplate.update( "INSERT INTO users (username, email) VALUES (?, ?)", (PreparedStatement ps) -> { ps.setString(1, user.getUsername()); ps.setString(2, user.getEmail()); } ); } ``` #### 2. **查询(Read)** ```java // 查询单个(需处理空结果异常) public User getUserById(Long id) { try { return jdbcTemplate.queryForObject( "SELECT * FROM users WHERE id = ?", BeanPropertyRowMapper.newInstance(User.class), id ); } catch (EmptyResultDataAccessException e) { return null; // 或抛出自定义异常 } } // 查询列表 public List<User> getAllUsers() { return jdbcTemplate.query( "SELECT id, username, email FROM users", BeanPropertyRowMapper.newInstance(User.class) ); } ``` #### 3. **更新(Update)** ```java public int updateEmail(Long userId, String newEmail) { return jdbcTemplate.update( "UPDATE users SET email = ? WHERE id = ?", newEmail, // 对应第一个? userId // 对应第二个? ); } // 更新整个对象 public int updateUser(User user) { return jdbcTemplate.update( "UPDATE users SET username=?, email=? WHERE id=?", user.getUsername(), user.getEmail(), user.getId() ); } ``` #### 4. **删除(Delete)** ```java public int deleteUser(Long userId) { return jdbcTemplate.update( "DELETE FROM users WHERE id = ?", userId ); } ``` --- ### 三、高级操作 #### 1. **批量插入** ```java public int[] batchInsert(List<User> users) { return jdbcTemplate.batchUpdate( "INSERT INTO users (username, email) VALUES (?, ?)", users.stream().map(user -> new Object[]{user.getUsername(), user.getEmail()}) .collect(Collectors.toList()) ); } ``` #### 2. **查询返回主键** ```java public Long insertAndReturnId(User user) { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement( "INSERT INTO users (username, email) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS ); ps.setString(1, user.getUsername()); ps.setString(2, user.getEmail()); return ps; }, keyHolder); return keyHolder.getKey().longValue(); } ``` --- ### 四、关键注意事项 1. **SQL注入防护**:始终使用`?`占位符,**不要拼接SQL字符串** 2. **事务控制**:在Service层添加`@Transactional`注解保证原子性 ```java @Transactional public void transferEmail(Long fromId, Long toId) { // 多个update操作 } ``` 3. **异常处理**: - `EmptyResultDataAccessException`:查询无结果时抛出 - `DataIntegrityViolationException`:违反约束(如唯一索引) 4. **性能优化**:高频操作建议复用`BeanPropertyRowMapper`实例 ```java private static final RowMapper<User> USER_MAPPER = BeanPropertyRowMapper.newInstance(User.class); ``` --- ### 五、完整工具类示例 ```java @Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; private static final RowMapper<User> USER_MAPPER = BeanPropertyRowMapper.newInstance(User.class); public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } // 实现上述CRUD方法... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值