【Neo4j系列】简化Neo4j数据库操作:一个基础工具类的开发之旅

news2024/9/28 1:20:02

作者:后端小肥肠

在Neo4j系列我打算写一个Neo4j同步关系数据库、增删改查及展示的基础小系统,这篇文件先分享系统里面的基础工具类,感兴趣的可以点个关注,看了文章的jym有更好的方法可以分享在评论区。

创作不易,未经允许严谨转载。

基础篇:

【Neo4j系列】Neo4j概念简介及整合SpringBoot_neo4j springboot-CSDN博客

目录

1. 前言

2. 与工具类适配的前端展示工具

2.1. neo4jd3.js 介绍

2.2. neo4jd3.js使用效果展示

3. Neo4j工具类包含功能简介

3.1. 编写 GraphDTO

3.2. 编写GraphNodeDTO

3.3. 编写GraphLinkDTO

3.4. 节点

3.4.1. 获取节点集合(无关系)

3.4.2. 获取单个节点

3.4.3. 获取所有节点(包含关系)

3.5. 获取所有标签

3.6. 数据库索引

3.6.1. 获取数据库索引

3.6.2. 创建 | 删除数据库索引

3.7. 获取所有关系

4. 工具类使用实战

4.1. 新增节点

4.2. 新增关系

4.3. 展示所有节点(包含关系)

5. 结语

6. 参考链接


1. 前言

Neo4j作为领先的图数据库,在处理复杂关系数据时展现出了卓越的性能和灵活性。然而,在实际开发中,我们常常会遇到一些重复性的任务和常见的挑战。为了提高开发效率,减少不必要的麻烦,一个强大而实用的工具类往往能够事半功倍。

在接下来的内容中,我们将详细介绍这个工具类的具体代码及核心功能。让我们一起探索如何让Neo4j开发变得更加高效和轻松吧!

如果这篇文章对你有帮助,别忘记动动小手点点关注哦~

2. 与工具类适配的前端展示工具

2.1. neo4jd3.js 介绍

neo4jd3.js 是一个基于 JavaScript 的库(源码地址:https://github.com/eisman/neo4jd3),它使用 D3.js 技术来可视化 Neo4j 图数据库中的数据。这种工具特别适合需要在 Web 环境中展示图形数据的应用场景。其核心优势包括:

  • 动态图形显示:用户可以看到图中的所有节点和边,并能通过拖拽和缩放来探索图中的元素。
  • 高度自定义:支持自定义节点和边的颜色、大小、形状和标签,使得每个项目都能够根据其具体需求来调整视图。
  • 交互性:提供点击节点查看详细信息,或是通过界面上的操作来进行图数据的动态查询和更新。

这些功能使得neo4jd3.js成为展示复杂关系和数据分析时的有力工具,帮助开发者和数据分析师更直观地理解和操作图数据库。

2.2. neo4jd3.js使用效果展示

下面是neo4jd3.js基础效果展示(无后端),先贴出html代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Force</title>
    <style>
        .nodetext {
            font-size: 12px;
            font-family: SimSun;
            fill: #000000;
        }
        .linetext {
            fill: #1f77b4;
            fill-opacity: 0.0;
        }
        .circleImg {
            stroke: #ff7f0e;
            stroke-width: 1.5px;
        }
    </style>
</head>
<body>
<h1>小肥肠知识图谱示例</h1>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
    var width = 900;
    var height = 800;
    var img_w = 77;
    var img_h = 80;
    var radius = 30; //圆形半径
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);
    var edges = [];
    d3.json("xfc.json", function(error, root) {
        if (error) {
            return console.log(error);
        }
        console.log(root);
        root.edges.forEach(function(e) {
            var sourceNode = root.nodes.filter(function(n) {
                return n.id === e.source;
            })[0],
            targetNode = root.nodes.filter(function(n) {
                return n.id === e.target;
            })[0];
            edges.push({
                source: sourceNode,
                target: targetNode,
                relation: e.type
            });
        });
        console.log(edges);
        
        var force = d3.layout.force()
            .nodes(root.nodes)
            .links(edges)
            .size([width, height])
            .linkDistance(200)
            .charge(-1500)
            .start();
        
        var defs = svg.append("defs");
        var arrowMarker = defs.append("marker")
            .attr("id", "arrow")
            .attr("markerUnits", "strokeWidth")
            .attr("markerWidth", "15")
            .attr("markerHeight", "15")
            .attr("viewBox", "0 0 12 12")
            .attr("refX", "17")
            .attr("refY", "6")
            .attr("orient", "auto");
        var arrow_path = "M2,2 L10,6 L2,10 L6,6 L2,2";
        arrowMarker.append("path")
            .attr("d", arrow_path)
            .attr("fill", "#ccc");
        
        var edges_line = svg.selectAll("line")
            .data(edges)
            .enter()
            .append("line")
            .attr("class", "line")
            .style("stroke", "#ddd")
            .style("stroke-width", 3)
            .attr("marker-end", "url(#arrow)");
        
        var edges_text = svg.selectAll(".linetext")
            .data(edges)
            .enter()
            .append("text")
            .attr("class", "linetext")
            .text(function(d) {
                return d.relation;
            })
            .style("fill-opacity", 1.0);
        
        var nodes_img = svg.selectAll("image")
            .data(root.nodes)
            .enter()
            .append("circle")
            .attr("class", "circleImg")
            .attr("r", radius)
            .attr("fill", function(d, i) {
                var defs = svg.append("defs").attr("id", "imgdefs");
                var catpattern = defs.append("pattern")
                    .attr("id", "catpattern" + i)
                    .attr("height", 1)
                    .attr("width", 1);
                catpattern.append("image")
                    .attr("x", -(img_w / 2 - radius))
                    .attr("y", -(img_h / 2 - radius))
                    .attr("width", img_w)
                    .attr("height", img_h)
                    .attr("xlink:href", d.labels);
                return "url(#catpattern" + i + ")";
            })
            .call(force.drag);
        
        var text_dx = -20;
        var text_dy = 20;
        var nodes_text = svg.selectAll(".nodetext")
            .data(root.nodes)
            .enter()
            .append("text")
            .attr("class", "nodetext")
            .attr("dx", text_dx)
            .attr("dy", text_dy)
            .text(function(d) {
                var uservalue = d.properties.username || "";
                var personvalue = d.properties.person || "";
                var phonevalue = d.properties.phone || "";
                return uservalue + phonevalue + personvalue;
            });
        
        force.on("tick", function() {
            root.nodes.forEach(function(d) {
                d.x = d.x - img_w / 2 < 0 ? img_w / 2 : d.x;
                d.x = d.x + img_w / 2 > width ? width - img_w / 2 : d.x;
                d.y = d.y - img_h / 2 < 0 ? img_h / 2 : d.y;
                d.y = d.y + img_h / 2 + text_dy > height ? height - img_h / 2 - text_dy : d.y;
            });
            edges_line.attr("x1", function(d) { return d.source.x; });
            edges_line.attr("y1", function(d) { return d.source.y; });
            edges_line.attr("x2", function(d) { return d.target.x; });
            edges_line.attr("y2", function(d) { return d.target.y; });
            edges_text.attr("x", function(d) { return (d.source.x + d.target.x) / 2; });
            edges_text.attr("y", function(d) { return (d.source.y + d.target.y) / 2; });
            nodes_img.attr("cx", function(d) { return d.x; });
            nodes_img.attr("cy", function(d) { return d.y; });
            nodes_text.attr("x", function(d) { return d.x; });
            nodes_text.attr("y", function(d) { return d.y + img_w / 2; });
        });
    });
</script>
</body>
</html>

通过上述代码,用户可以在网页上看到一个动态的知识图谱,节点以图像的形式展示,节点之间的关系通过带箭头的线条表示,并且每条线上显示关系类型。用户可以通过拖动节点来重新排列图谱,从而更直观地理解数据之间的关联性。

xfc.json文件:

{
  "nodes": [
    {
      "id": "2",
      "labels": "./image/kn.png",
      "properties": {
        "person": "康娜1号"
      }
    },
    {
      "id": "58688",
      "labels": "./image/kn2.png",
      "properties": {
        "person": "康娜2号"
      }
    },
    {
      "id": "128386",
      "labels": "./image/kn3.png",
      "properties": {
        "person": "小肥肠"
      }
    },
    {
      "id": "200000",
      "labels": "./image/kn4.png",
      "properties": {
        "person": "人物4号"
      }
    },
    {
      "id": "300000",
      "labels": "./image/kn5.png",
      "properties": {
        "person": "人物5号"
      }
    },
    {
      "id": "400000",
      "labels": "./image/kn6.png",
      "properties": {
        "person": "人物6号"
      }
    },
    {
      "id": "500000",
      "labels": "./image/kn7.png",
      "properties": {
        "person": "人物7号"
      }
    },
    {
      "id": "600000",
      "labels": "./image/kn8.png",
      "properties": {
        "person": "人物8号"
      }
    }
  ],
  "edges": [
    {
      "id": "23943",
      "type": "know",
      "source": "2",
      "target": "58688",
      "properties": {}
    },
    {
      "id": "94198",
      "type": "know",
      "source": "58688",
      "target": "128386",
      "properties": {}
    },
    {
      "id": "1000000",
      "type": "know",
      "source": "128386",
      "target": "200000",
      "properties": {}
    },
    {
      "id": "1100000",
      "type": "know",
      "source": "200000",
      "target": "300000",
      "properties": {}
    },
    {
      "id": "1200000",
      "type": "know",
      "source": "300000",
      "target": "400000",
      "properties": {}
    },
    {
      "id": "1300000",
      "type": "know",
      "source": "400000",
      "target": "500000",
      "properties": {}
    },
    {
      "id": "1400000",
      "type": "know",
      "source": "500000",
      "target": "600000",
      "properties": {}
    }
  ]
}

效果展示:

效果还是不错的吧,请忽略我这个略丑的demo界面,之后的系统肯定会呈现一个完美的功能界面的,现在只是给大家打个样。 

3. Neo4j工具类包含功能简介

工具类的构建参考了github上的一个项目(https://github.com/MiracleTanC/Neo4j-KGBuilder),我在他的基础上做了稍微的修改,工具类包含返回节点集合、获取所有标签、获取数据库索引、删除索引、创建索引、返回关系、返回节点和关系、返回单个节点的信息等(后续文章会放出全部代码)。

ps:我的工具类是要适配前端的neo4jd3.js,所以工具类里面有数据格式的部分都是对应neo4jd3.js所要求的格式。

3.1. 编写 GraphDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphDTO {
    private List<GraphNodeDTO>nodes;
    private List<GraphLinkDTO>edges;
}

3.2. 编写GraphNodeDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphNodeDTO {
    private String id;
    private String labels;
    private HashMap<String,Object> properties;
}

3.3. 编写GraphLinkDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GraphLinkDTO {
    private String id;
    private String type;
    private String source;
    private String target;
    private HashMap<String,Object> properties;
}

3.4. 节点

3.4.1. 获取节点集合(无关系)
    public static List<GraphNodeDTO> getGraphNode(String cypherSql) {
        log.debug("Executing Cypher query: {}", cypherSql);

        try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {
            return session.writeTransaction(tx -> {
                Result result = tx.run(cypherSql);
                return result.list().stream()
                        .flatMap(record -> record.fields().stream())
                        .filter(pair -> pair.value().type().name().equals("NODE"))
                        .map(pair -> {
                            Node node = pair.value().asNode();
                            // label为节点对应图片
                            String label = "";
                            HashMap<String, Object> properties = new HashMap<>(node.asMap());
                            return new GraphNodeDTO(String.valueOf(node.id()), label, properties);
                        })
                        .collect(Collectors.toList());
            });
        } catch (Exception e) {
            log.error("Error executing Cypher query: {}", cypherSql, e);
            return new ArrayList<>();
        }
    }

这段代码定义了一个名为 getGraphNode 的Java方法,它用于执行一个Neo4j Cypher查询来获取节点,并将结果封装为 GraphNodeDTO 对象的列表。该方法首先设置会话为写入模式,确保能执行可能涉及写入的查询。使用 session.writeTransaction 确保查询在事务中执行,这有助于处理可能的并发修改和保证数据一致性。

3.4.2. 获取单个节点
    public static GraphNodeDTO getSingleGraphNode(String cypherSql) {
        List<GraphNodeDTO> nodes = getGraphNode(cypherSql);
        if(CollectionUtils.isNotEmpty(nodes)){
            return nodes.get(0);
        }
        return null;
    }

 getSingleGraphNode 方法从数据库中执行指定的 Cypher 查询,获取查询结果中的第一个图谱节点。如果结果列表不为空,它会返回第一个节点;如果列表为空,则返回 null。这个方法简单明了,旨在快速获取一个节点或确认没有结果。

3.4.3. 获取所有节点(包含关系)
    public static GraphDTO getGraphNodeAndShip(String cypherSql) {
        log.debug("Executing Cypher query: {}", cypherSql);
        GraphDTO graphDTO = new GraphDTO();
        try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {
            return session.writeTransaction(tx -> {
                Result result = tx.run(cypherSql);
                List<GraphNodeDTO> nodes = new ArrayList<>();
                List<GraphLinkDTO> relationships = new ArrayList<>();
                HashSet<String> uuids = new HashSet<>();

                result.list().stream().flatMap(record -> record.fields().stream()).forEach(pair -> {
                    String type = pair.value().type().name();
                    if ("NODE".equals(type)) {
                        Node node = pair.value().asNode();
                        String uuid = String.valueOf(node.id());
                        if (uuids.add(uuid)) {
                            HashMap<String, Object> properties = new HashMap<>(node.asMap());
                            nodes.add(new GraphNodeDTO(uuid, node.labels().iterator().next(), properties));
                        }
                    } else if ("RELATIONSHIP".equals(type)) {
                        Relationship relationship = pair.value().asRelationship();
                        HashMap<String, Object> properties = new HashMap<>(relationship.asMap());
                        relationships.add(new GraphLinkDTO(String.valueOf(relationship.id()), relationship.type(),
                                String.valueOf(relationship.startNodeId()),
                                String.valueOf(relationship.endNodeId()),
                                properties));
                    }
                });

                graphDTO.setNodes(nodes);
                graphDTO.setEdges(toDistinctList(relationships));
                return graphDTO;
            });
        } catch (Exception e) {
            log.error("Error executing Cypher query: {}", cypherSql, e);
            return new GraphDTO();
        }
    }

这段代码定义了一个名为 getGraphNodeAndShip 的Java方法,用于在Neo4j数据库中执行Cypher查询,从而获取并处理节点和关系数据,将结果封装成 GraphDTO 对象。它使用了写入事务来确保操作的稳定性,并在出现异常时进行了错误记录,最终返回一个包含节点和关系的复合数据对象。

3.5. 获取所有标签

public static List<HashMap<String, Object>> getGraphLabels() {
    String cypherSql = "CALL db.labels()";
    log.debug("Executing Cypher query: {}", cypherSql);

    try (Session session = neo4jDriver.session()) {
        return session.readTransaction(tx -> {
            Result result = tx.run(cypherSql);
            return result.list().stream()
                    .map(record -> {
                        HashMap<String, Object> labelInfo = new HashMap<>();
                        record.fields().forEach(pair -> {
                            String key = pair.key();
                            Value value = pair.value();
                            if (key.equalsIgnoreCase("label")) {
                                String label = value.asString().replace("\"", "");
                                labelInfo.put(key, label);
                            } else {
                                labelInfo.put(key, value.asObject());
                            }
                        });
                        return labelInfo;
                    })
                    .collect(Collectors.toList());
        });
    } catch (Exception e) {
        log.error("Error executing Cypher query for graph labels", e);
        return new ArrayList<>();
    }
}

这段代码用于从 Neo4j 数据库中检索所有图谱标签。它执行 CALL db.labels() 查询,并将结果转换为 List<HashMap<String, Object>> 格式。每个标签信息以 HashMap 的形式存储,其中包含标签的键和值。如果查询过程中发生异常,代码会记录错误并返回一个空列表。 

3.6. 数据库索引

3.6.1. 获取数据库索引
    public static List<HashMap<String, Object>> getGraphIndex() {
        String cypherSql = "CALL db.indexes()";
        log.debug("Executing Cypher query: {}", cypherSql);

        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Result result = tx.run(cypherSql);
                return result.list().stream()
                        .map(record -> {
                            HashMap<String, Object> indexInfo = new HashMap<>();
                            record.fields().forEach(pair -> {
                                String key = pair.key();
                                Value value = pair.value();
                                if (key.equalsIgnoreCase("labelsOrTypes")) {
                                    String objects = value.asList().stream()
                                            .map(Object::toString)
                                            .collect(Collectors.joining(","));
                                    indexInfo.put(key, objects);
                                } else {
                                    indexInfo.put(key, value.asObject());
                                }
                            });
                            return indexInfo;
                        })
                        .collect(Collectors.toList());
            });
        } catch (Exception e) {
            log.error("Error executing Cypher query for index information", e);
            return new ArrayList<>();
        }
    }

这个方法执行 Cypher 查询 CALL db.indexes() 来获取数据库索引信息。它记录查询语句,然后在 Neo4j 会话中执行查询并处理结果。对于每个索引信息记录,方法将其字段转换为 HashMap,特别处理 labelsOrTypes 字段,将其值转换为逗号分隔的字符串。最终,它将所有索引的 HashMap 收集到一个列表中并返回。如果出现异常,则记录错误并返回一个空列表。

3.6.2. 创建 | 删除数据库索引
    /**
     * 删除索引
     * @param label
     */
    public static void deleteIndex(String label) {
        try (Session session = neo4jDriver.session()) {
            String cypherSql=String.format("DROP INDEX ON :`%s`(name)",label);
            session.run(cypherSql);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 创建索引
     * @param label
     * @param prop
     */
    public static void createIndex(String label,String prop) {
        try (Session session = neo4jDriver.session()) {
            String cypherSql=String.format("CREATE INDEX ON :`%s`(%s)",label,prop);
            session.run(cypherSql);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

这段代码包含两个方法,deleteIndexcreateIndex,分别用于在 Neo4j 数据库中删除和创建索引。deleteIndex 方法根据提供的标签删除该标签上的 name 索引,而 createIndex 方法在指定标签的指定属性上创建一个新索引。两者都使用 Neo4j 的 Cypher 查询语句执行操作,并在发生异常时记录错误信息。 

3.7. 获取所有关系

    public static List<GraphLinkDTO> getGraphRelationship(String cypherSql) {
        log.debug("Executing Cypher query: {}", cypherSql);

        try (Session session = neo4jDriver.session(SessionConfig.builder().withDefaultAccessMode(AccessMode.WRITE).build())) {
            return session.writeTransaction(tx -> {
                Result result = tx.run(cypherSql);
                return result.list().stream()
                        .flatMap(record -> record.fields().stream())
                        .filter(pair -> pair.value().type().name().equals("RELATIONSHIP"))
                        .map(pair -> {
                            Relationship relationship = pair.value().asRelationship();
                            String id = String.valueOf(relationship.id());
                            String source = String.valueOf(relationship.startNodeId());
                            String target = String.valueOf(relationship.endNodeId());
                            HashMap<String, Object> properties = new HashMap<>(relationship.asMap());
                            return new GraphLinkDTO(id, relationship.type(), source, target, properties);
                        })
                        .collect(Collectors.toList());
            });
        } catch (Exception e) {
            log.error("Error executing Cypher query: {}", cypherSql, e);
            return new ArrayList<>();
        }
    }

这段代码定义了一个名为 getGraphRelationship 的方法,它执行一个Neo4j Cypher查询以获取关系数据,并将每个关系转换为 GraphLinkDTO 对象。方法在写入事务中执行查询,确保稳定性,并使用流处理来提取和封装关系的详细信息,包括ID、类型、起始节点、目标节点和属性。如果查询执行过程中发生异常,会记录错误并返回一个空列表

4. 工具类使用实战

4.1. 新增节点

参数:

代码:

    public GraphDTO createNode(AddNodeDTO addNodeDTO) {
        GraphDTO dataGraphDTO = new GraphDTO();
        String cypherSql = String.format("n.name='%s'", addNodeDTO.getProperties().get("name"));
        String buildNodeql = "";
        buildNodeql = String.format("create (n:`%s`) set %s return n", addNodeDTO.getDomain(), cypherSql);
        List<GraphNodeDTO> nodes = Neo4jUtil.getGraphNode(buildNodeql);
        dataGraphDTO.setNodes(nodes);
        return dataGraphDTO;
    }

4.2. 新增关系

参数:

代码:

    public GraphDTO createLink(AddLinkDTO addLinkDTO) {
        String cypherSql = String.format("MATCH (n:`%s`),(m:`%s`) WHERE id(n)=%s AND id(m) = %s "
                + "CREATE (n)-[r:%s]->(m)" + "RETURN n,m,r", addLinkDTO.getDomain(), addLinkDTO.getDomain(), addLinkDTO.getSource(), addLinkDTO.getTarget(), addLinkDTO.getType());
        return   Neo4jUtil.getGraphNodeAndShip(cypherSql);
        
    }

4.3. 展示所有节点(包含关系)

    public GraphDTO getGraph(String domain) {
        GraphDTO graphDTO = new GraphDTO();
        if (!StringUtil.isBlank(domain)) {
            String nodeSql = String.format("MATCH (n:`%s`)  RETURN distinct(n) ", domain);
            List<GraphNodeDTO> graphNode = Neo4jUtil.getGraphNode(nodeSql);
            graphDTO.setNodes(graphNode);
            String relationShipSql = String.format("MATCH (n:`%s`)<-[r]-> (m)  RETURN distinct(r) ", domain);// m是否加领域
            List<GraphLinkDTO> graphRelation = Neo4jUtil.getGraphRelationship(relationShipSql);
            graphDTO.setEdges(graphRelation);

        }

效果图: 

5. 结语

在本文中介绍了面向neo4jd3.js的后端neo4j工具类。本工具不仅简化了图数据库的操作,还通过GraphDTO, GraphNodeDTO, 和 GraphLinkDTO等类,有效地封装和传输图数据。同时演示了如何使用这些工具执行常见的图操作,包括新增节点和关系,以及展示关系数据,下一章节将介绍如何在实际场景中实现neo4j与关系数据库实现数据同步,感兴趣的话可动动小手点点关注~

6. 参考链接

基于d3.js/neovis.js/neod3.js实现链接neo4j图形数据库的图像化显示功能_javascript技巧_脚本之家

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2052670.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《勇敢的哈克》开发商:为Xbox移植游戏是一场噩梦

国产类银河恶魔城游戏《勇敢的哈克》开发商Blingame在尝试将游戏移植到Xbox上时&#xff0c;遭遇让人头痛的技术障碍&#xff0c;最终他们只能放弃。《勇敢的哈克》是一款融合了类银河恶魔城元素的末日废土风平台动作游戏。你需要利用多功能能量钩爪组合出独特技能&#xff0c;…

Jenkins使用Publish Over SSH插件远程部署程序到阿里云服务器

前言 使用Jenkins远程构建springboot服务到阿里云上&#xff0c;Jenkins版本&#xff1a;Version 2.462.1 1、准备 在可选插件中&#xff0c;搜索Maven Integration &#xff0c;Gitlab和 Publish Over SSH 三个插件&#xff1a; 如果需要多用户管理那就安装&#xff1a;Ro…

【MySQL进阶之路】表结构的操作

目录 创建表 查看表 查看数据库有哪些表 查看表结构 查看表的详细信息 修改表 表的重命名 添加一列 修改某一列的属性 删除某一列 对列进行重命名 删除表 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 【MySQL进阶之路】MySQL基础——从零认识MySQL-CSDN博客 创…

LLM技术揭秘:Qwen的Function Calling实现解析

简介 Function Calling 是一种让 Chat Completion 模型调用外部函数的能力&#xff0c;可以让模型不仅仅根据自身的数据库知识进行回答&#xff0c;而是可以额外挂载一个函数库&#xff0c;然后根据用户提问去函数库检索&#xff0c;按照实际需求调用外部函数并获取函数运行结…

授权cleanmymac访问全部磁盘 Mac授权访问权限 cleanmymac缺少权限

CleanMyMac是Mac系统下的一款专业的苹果电脑清理软件&#xff0c;同时也是一款优秀的电脑系统管理软件。它能有效清理系统垃圾&#xff0c;快速释放磁盘内存&#xff0c;缓解卡顿现象&#xff0c;保障系统顺畅地运行。 全磁盘访问权限&#xff0c;就好比机场内进行的安全检查。…

Vue3页面组件化开发

本节讲解Vue3页面组件化开发的原因和操作 1.原因 1.业务需求 2.页面展示 根据菜单选项展示不同的页面布局和页面信息 3.页面源码 <template><div class"box"><div class"UserInfo"><div class"UserImg"><img src&…

云计算第三阶段---DBA数据库管理

Day1 一.数据库概述 计算机中数据存放的仓库&#xff0c;可以按数据类型划分数据库&#xff0c;又可以在数据库中划分更细节的分类存放到相应的表中。 抽象来说就是生活中的菜市场、五金店之类的场所&#xff0c;根据用途开设&#xff1b;划分广泛的还有百货商场、批发市场等…

HarmonyOS应用程序访问控制探究

关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 一、引言 随着信息技术的飞速发展&#xff0c;移动应用程序已经成为…

【C++】实现日期类相关接口

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇 本篇将介绍实现日期类中众多接口&#xff0c;同时这其中涉及到很多知识&#xff0c;务必将类和对象相关内容掌握再来实现日期类…

【数据结构】详细介绍栈和队列,解析栈和队列每一处细节

目录 一. 栈 1. 栈的概念 2. 栈的实现 2.1 栈的结构 2.2 初始化栈 2.3 入栈 2.4 出栈 2.5 获取栈顶元素 2.6 获取栈中有效个数 2.7 判断栈是否为空 2.8 销毁栈 二. 队列 1. 队列的概念 2. 队列的实现 2.1 队列的结构 2.2 队列初始化 2.3 销毁队列 2.4 入…

聊聊适配器模式

目录 适配器模式概念 主要实现方式 主要组成 UML用例图 代码示例 生活场景 应用场景 适配器模式概念 适配器模式属于结构型设计模式&#xff0c;它的主要目的是将一个类的接口转换成客户端所期望的另一种接口形式&#xff0c;使得原本接口不兼容的类可以一起工作。 主…

【New SQL】 -- CockroachDB license change

1、CockroachDB 发布了修改开源协议的 releases 北京时间 2024-08-16 &#xff0c;CockroachDB 发布了修改开源协议的 releases。 原文链接&#xff1a;Licensing FAQs Evolving our self-hosted offering and license model CockroachDB License change (again) | Product T…

Kali Linux 定制化魔改 添加常见60渗透工具

项目地址&#xff1a;https://github.com/CuriousLearnerDev/kali-linux-kde-beautify 系统版本&#xff1a;kali linux 2024.1 固件类型&#xff1a;BIOS 用户: zss 密码: ss 完整版 系统压缩大小&#xff1a;18.8 GB 解出来&#xff1a;36.00GB 左右 系统版 系统压缩大小&…

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 04路由协议的选择

本章要回答的问题&#xff1a; 路由是如何工作的?有哪些类型的路由协议?Clos 拓扑中的路由协议是如何工作的?什么是无编号接口&#xff0c;以及为什么无编号接口如此重要?如何确定最适合自己需求的路由协议? 路由概述 用最简单的话来说&#xff0c;路由是使用数据包的目…

DESeq2差异基因分析和批次效应移除

差异基因鉴定 基因表达标准化 不同样品的测序量会有差异&#xff0c;最简单的标准化方式是计算counts per million (CPM)&#xff0c;即原始reads count除以总reads数乘以1,000,000。 这种计算方式的缺点是容易受到极高表达且在不同样品中存在差异表达的基因的影响&#xff…

FunClip,音视频识别,自动化剪辑,文本校对,智能纠错,导出SRT

今天给大家介绍一个自动化剪辑项目——FunClip,该项目是由阿里开源的&#xff0c;可以识别音频、视频中的文字&#xff0c;一键剪辑和添加字幕。 FunClip是一款高效的自动化视频剪辑工具&#xff0c;它利用语音识别技术对视频内容进行分析。用户可以在识别结果中选择所需的文本…

重生奇迹MU 梦幻骑士 真正生而高贵的职业

作为重生奇迹MU梦幻骑士中真正生而高贵的职业&#xff0c;圣骑士是玩家们最为追捧的职业之一。在游戏内&#xff0c;圣骑士拥有着强大的防御和治疗能力&#xff0c;成为团队中不可或缺的存在。如果你正准备选择一个强大的职业&#xff0c;那么不妨考虑成为一名圣骑士&#xff0…

选择文件摆渡系统要遵守的“三要”和“三不要”原则

文件摆渡系统不仅可以实现企业网络隔离后的数据摆渡需求&#xff0c;同时也可以视作企业数据安全及网络安全建设的重要组成部分。文件摆渡系统的选择也很关键&#xff0c;在企业进行筛选时&#xff0c;应该遵守“三要”和“三不要”原则。 “三要”之一&#xff1a;要安全 文件…

MySQL对事务的支持

5.MySQL对事务的支持情况&#xff1a; 5.1. 查看存储引擎对事务的支持情况 &#xff1a; 1.SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些&#xff0c;以及这些存储引擎是否支持事务2.下图可以能看出在 MySQL 中&#xff0c;只有InnoDB类型的存储引擎是支持事务…

轻松搞定 Java7 新特性,示例丰富

Java 7 是 Java 语言的一个主要版本&#xff0c;于 2011 年 7 月 28 日正式发布。 由 Sun Microsystems 开发并由 Oracle 公司发布的一个重要版本&#xff0c;它带来了许多新功能特性&#xff0c;增强了编程语言的能力和性能。 以下是一些 Java 7 的主要新功能特性&#xff1…