代码编织梦想

1.新建一个SpringBoot项目,向pom.xml中添加相关依赖:

<!--配置数据库JPA依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--通过maven坐标引入JTA atomikos-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>
        <!--配置dozer依赖-->
        <dependency>
            <groupId>com.github.dozermapper</groupId>
            <artifactId>dozer-spring-boot-starter</artifactId>
            <version>6.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

 其中项目中用到的比较重要的是:JTA  Atomikos依赖和jpa依赖。

2.在全局配置文件(application)中对数据库进行配置:

server:
  port: 8888
spring:
  datasource:
    primary:
      url: jdbc:mysql://127.0.0.1:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://127.0.0.1:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:                           #添加SpringDataJPA配置
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: update
    database: mysql
    show-sql: true
  jta:                                 #添加分布式JTA配置
    atomikos:
      datasource:
        max-pool-size: 20
        borrow-connection-timeout: 60
      connectionfactory:
        max-pool-size: 20
        borrow-connection-timeout: 60

2.1。几点说明:(1)port为端口号;(2)datasource:为所需要操作的两个数据源,分别是primary和secondary;(3)jpa就是添加所使用到的SpringDataJPA配置,其中比较重要的就是ddl-auto它共有四个参数分别是,《1》 create:表示每次加载hibernate的时候,都会删除上一次生成的表,然后再根据你的modle类生成一个表 《2》create-drop:每次加载hibernate的时候,根据你的modle类生成一个表,当sessionFactory关闭的时候,表就自动删除 《3》update:hibernate根据你的modle类创建表结构,如果该表没有,则会创建,如果存在就进行根新表结构,并不会删除表里面以前存在的数据 《4》validate:每次加载hibernate的时候,都会验证表结构是否和modle类的定义一致,如不不一致,就会抛出异常,不会做出修改数据库的动作;我们为了安全,一般就默认使用validate。其他的都是固定搭配;(4)分布式事务jta atomikos中的一些配置:初始化数据库链接池,包括最大的数据库链接数和最大的超时时间。

3.jpa操作多数据源,并实现分布式事务原理,就是给定一个总的管理类,来进行一个统一的管理,这是我的理解。

 

 在理解并写代码时,可以对着上图。

 4,在写业务类时,一般及时模板了。

 4.1写第一层Dao层,也就是持久层,专注于对数据的持久化操作,例如将数据存入数据库,磁盘等。

(1)Dao层的一个结构:

 (2)对应的代码:

《1》Article

package com.example.jpamultipledatasource.Dao.testdb;
import lombok.Builder;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
 * @author ZEShart
 * @create 2021-03-03-12:40
 */
@Data
@Builder
@Entity     //表示当前类是一个实体类,并表示接受SpringDataJPA的控制管理
//如果不特定指哪个表,就默认  类名对应的表
//反之,用注解@Table指定对应特定的表
@Table(name = "article")
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;   //id是唯一的主键并且是自增的类型是IDENTITY,所以加如上两个注解
    @Column(nullable = false,length=32)
    private String author;
    @Column(nullable = false,unique = true,length=32)
    private String title;
    @Column(length=512)
    private String content;
    private Date createTime;
}

《2》对应的   ArticleRepository

package com.example.jpamultipledatasource.Dao.testdb;
import org.springframework.data.jpa.repository.JpaRepository;
/**
 * @author ZEShart
 * @create 2021-03-26-17:05
 */
public interface ArticleRepository extends JpaRepository<Article,Long> {
    //<Article,Long>  Article表示要操作的数据库表对应实体PO,Long  是id属性实体主键的类型

    //注意这个方法的名称,JPA会根据方法名自动生成SQL语句
    Article findByAuthor(String author);    //可以所很智能了
}

《3》Message

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
 * @author ZEShart
 * @create 2021-03-27-20:01
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Message {
    @Id
    @GeneratedValue
    private int id;
    @Column(nullable = false)
    private String name;
    @Column(nullable = false)
    private String content;
}

《4》对应的   MessageRepository

package com.example.jpamultipledatasource.Dao.testdb2;

import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author ZEShart
 * @create 2021-03-27-20:19
 */
public interface MessageRepository extends JpaRepository<Message,Long> {

}

4.2写第二层service层,这样是为了避免,controller层直接操作dao层,不安全。

 (1) service层的一个结构:

(2)对应的代码:

 《1》ArticleService

package com.example.jpamultipledatasource.service;
import com.example.jpamultipledatasource.modle.ArticleVO;

import java.util.List;

/**
 * @author ZEShart
 * @create 2021-03-26-23:38
 */
public interface ArticleService {
    void saveArticle(ArticleVO articleVO);
    void deleteArticle(Long id);
    void updateArticle(ArticleVO articleVO);
    ArticleVO getArticle(Long id);
    List<ArticleVO> getAll();
}

《2》ArticleJPAServiceImpl

package com.example.jpamultipledatasource.service;

import com.example.jpamultipledatasource.Dao.testdb.Article;
import com.example.jpamultipledatasource.Dao.testdb.ArticleRepository;
import com.example.jpamultipledatasource.Dao.testdb2.Message;
import com.example.jpamultipledatasource.Dao.testdb2.MessageRepository;
import com.example.jpamultipledatasource.modle.ArticleVO;
import com.example.jpamultipledatasource.utils.DozerUtils;
import org.dozer.Mapper;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author ZEShart
 * @create 2021-03-28-10:48
 */
public class ArticleJPAServiceImpl implements ArticleService{
    //将JPA仓库对象注入
    @Resource
    private ArticleRepository articleRepository;
    //将JPA仓库对象注入
    @Resource
    private MessageRepository messageRepository;
    @Resource
    private Mapper dozerMapper;

    @Transactional
    public void saveArticle(ArticleVO articleVO) {
        //将articleVO转换为articlePO,使用dozer或者springUtils
        Article articlePO=dozerMapper.map(articleVO,Article.class);
        articleRepository.save(articlePO);
        messageRepository.save(new Message(1,"zimug","爱学习"));
    }

    @Override
    public void deleteArticle(Long id) {
        articleRepository.deleteById(id);
    }


    @Override
    public void updateArticle(ArticleVO articleVO) {
        //将articleVO转换为articlePO,使用dozer或者springUtils
        Article articlePO=dozerMapper.map(articleVO,Article.class);
        articleRepository.save(articlePO);//新增和保存使用的是同一个函数方法save()
        //articleJDBCDAO.updateById(article,secondaryJdbcTemplate);
    }

    @Override
    public ArticleVO getArticle(Long id) {
        Optional<Article> article=articleRepository.findById(id);
        return dozerMapper.map(article.get(),ArticleVO.class);
    }


    @Override
    public List<ArticleVO> getAll() {
        List<Article> articleLis=articleRepository.findAll();
        return DozerUtils.mapList(articleLis,ArticleVO.class);
    }
}

 5.这里就不写controller层了,写的是配置类config层,这一层一般都是固定搭配,唯一要改的就是你的包路径。

(1)对应的结构

(2)对应的代码:

《1》JPAPrimaryConfig

package com.example.jpamultipledatasource.config;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
/**
 * @author ZEShart
 * @create 2021-03-27-20:23
 */
@Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(basePackages = "com.example.jpamultipledatasource.Dao.testdb",  //注意这里
        entityManagerFactoryRef = "primaryEntityManager",
        transactionManagerRef = "transactionManager")
public class JPAPrimaryConfig {
    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;

    //primary
    @Primary
    @Bean(name = "primaryDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.primary")     //注意这里
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean(name = "primaryDataSource", initMethod = "init", destroyMethod = "close")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(primaryDataSourceProperties().getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(primaryDataSourceProperties().getPassword());
        mysqlXaDataSource.setUser(primaryDataSourceProperties().getUsername());
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("primary");
        xaDataSource.setBorrowConnectionTimeout(60);
        xaDataSource.setMaxPoolSize(20);
        return xaDataSource;
    }

    @Primary
    @Bean(name = "primaryEntityManager")
    @DependsOn("transactionManager")
    public LocalContainerEntityManagerFactoryBean primaryEntityManager() throws Throwable {

        HashMap<String, Object> properties = new HashMap<String, Object>();
        properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
        properties.put("javax.persistence.transactionType", "JTA");
        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setJtaDataSource(primaryDataSource());
        entityManager.setJpaVendorAdapter(jpaVendorAdapter);
        //这里要修改成主数据源的扫描包
        entityManager.setPackagesToScan("com.example.jpamultipledatasource.Dao.testdb");
        entityManager.setPersistenceUnitName("primaryPersistenceUnit");
        entityManager.setJpaPropertyMap(properties);
        return entityManager;
    }
}

《2》JPASecondaryConfig

package com.example.jpamultipledatasource.config;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
/**
 * @author ZEShart
 * @create 2021-03-27-20:34
 */
@Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(basePackages = "com.example.jpamultipledatasource.Dao.testdb2",   //注意这里
        entityManagerFactoryRef = "secondaryEntityManager",
        transactionManagerRef = "transactionManager")
public class JPASecondaryConfig {

    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;


    @Bean(name = "secondaryDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")    //注意这里
    public DataSourceProperties masterDataSourceProperties() {
        return new DataSourceProperties();
    }


    @Bean(name = "secondaryDataSource", initMethod = "init", destroyMethod = "close")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource masterDataSource() throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(masterDataSourceProperties().getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(masterDataSourceProperties().getPassword());
        mysqlXaDataSource.setUser(masterDataSourceProperties().getUsername());
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("secondary");
        xaDataSource.setBorrowConnectionTimeout(60);
        xaDataSource.setMaxPoolSize(20);
        return xaDataSource;
    }

    @Bean(name = "secondaryEntityManager")
    @DependsOn("transactionManager")
    public LocalContainerEntityManagerFactoryBean masterEntityManager() throws Throwable {

        HashMap<String, Object> properties = new HashMap<String, Object>();
        properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
        properties.put("javax.persistence.transactionType", "JTA");
        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setJtaDataSource(masterDataSource());
        entityManager.setJpaVendorAdapter(jpaVendorAdapter);
        //这里要修改成主数据源的扫描包
        entityManager.setPackagesToScan("com.example.jpamultipledatasource.Dao.testdb2");
        entityManager.setPersistenceUnitName("secondaryPersistenceUnit");
        entityManager.setJpaPropertyMap(properties);
        return entityManager;
    }
}

《3》JPAAtomikosTransactionConfig

package com.example.jpamultipledatasource.config;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;

import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

/**
 * @author ZEShart
 * @create 2021-03-28-22:02
 */
@Configuration
@ComponentScan
@EnableTransactionManagement
public class JPAAtomikosTransactionConfig {
    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
    //设置JPA特性
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        //显示sql
        hibernateJpaVendorAdapter.setShowSql(true);
        //自动生成/更新表
        hibernateJpaVendorAdapter.setGenerateDdl(true);
        //设置数据库类型
        hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
        return hibernateJpaVendorAdapter;
    }

    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }

    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public TransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        AtomikosJtaPlatform.transactionManager = userTransactionManager;
        return userTransactionManager;
    }

    @Bean(name = "transactionManager")
    @DependsOn({"userTransaction", "atomikosTransactionManager"})
    public PlatformTransactionManager transactionManager() throws Throwable {
        UserTransaction userTransaction = userTransaction();
        AtomikosJtaPlatform.transaction = userTransaction;
        TransactionManager atomikosTransactionManager = atomikosTransactionManager();
        return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
    }
}

《4》AtomikosJtaPlatform

package com.example.jpamultipledatasource.config;
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
/**
 * @author ZEShart
 * @create 2021-03-28-11:00
 */
public class AtomikosJtaPlatform extends AbstractJtaPlatform{
    private static final long serialVersionUID = 1L;

    static TransactionManager transactionManager;
    static UserTransaction transaction;

    @Override
    protected TransactionManager locateTransactionManager() {
        return transactionManager;
    }

    @Override
    protected UserTransaction locateUserTransaction() {
        return transaction;
    }
}

6.写的是一个工具包

(1)结构如下:

(2)DozerUtils

package com.example.jpamultipledatasource.utils;

import org.assertj.core.util.Lists;
import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @author ZEShart
 * @create 2021-03-26-17:26
 */
public class DozerUtils {
    static Mapper mapper= DozerBeanMapperBuilder.buildDefault();
    public static<T> List<T> mapList(Collection sourceList,Class<T> destinationClass){
        List destinationList=Lists.newArrayList();
        for (Iterator i$ = sourceList.iterator(); i$.hasNext();){
            Object sourceObject=i$.next();
            Object destinationObject=mapper.map(sourceObject,destinationClass);
            destinationList.add(destinationObject);
        }
        return destinationList;
    }
}

7.测试可以发现,要失败时,两个对数据库的操作都失败,要成功是都成功,即jpa+jta+atomokis分布式事务测试成功。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Zongzhe520/article/details/115303131

spring data jpa 6. @modifying 注解和事务_gardenerhan的博客-爱代码爱编程

@Modifying 注解和事务 @Query 与 @Modifying 执行更新操作 @Query 与 @Modifying 这两个 annotation一起声明,可定义个性化更新操作,例如只涉及某些字段更新时最为常用

spring data jpa事务支持_chengqiuming的博客-爱代码爱编程

一 点睛 Spring Data JPA对所有默认方法都开启了事务支持,且查询类事务默认启用readOnly=true属性。 二 SimpleJpaRepository缩减版源码 @Repository @Transactional(readOnly = true) public class SimpleJpaRepository<T, ID

spring boot,jpa和atomikos实现分布式事务_hongfu951的博客-爱代码爱编程

一、实例描述和实体模型      我们想在同一时间两个不同的数据库保存两个实体,这个操作需要事务。因此,在这个例子中,我们有一个Customer实体,它将第一个持久化到数据库中,而Order实体将被持久化到第二个数据库中。这两个实体非常简单,这个实例仅仅是一个示范。 这个结果实现如下:值得注意的是,它是属于两个不同的包,原因有两点: 1、项目呈现

spring boot 使用jpa @transactional 报错事务不回滚_jey_4的博客-爱代码爱编程

@Transactional有两个包: 1、org.springframework.transaction.annotation.Transactional; 2、javax.transaction.Transactional; 两个过都试过了,但service层方法上报错后jpa.save方法仍然插入了数据到DB中,没有回滚,看用的mysql(5.

18.从零开始学springboot-jpa-atomikos多数据源分布式事务案例-爱代码爱编程

前言 前章我们已经能够流畅的写出一个基于springboot2.1.3的多数据源的案例了,而且我们选择很多,可以通过jpa搭建,也可以通过jdbc。有了多数据源,必然会碰到多数据源事务处理的问题,也就是分布式事务,所以,这

springboot+jpa+jta+atomikos十分钟实现分布式事务,模拟多数据源-爱代码爱编程

基于jta+atomikos解决分布式事务 模拟多数据源 jta: Java Transactio API,即是java中对事务处理的api,api即是接口的意思. atomikos:Atomikos Transactio

请教:什么原因会导致 springboot 里jpa调用会开启两个数据库事务?-爱代码爱编程

一个 springboot + jpa 的项目,发现里面只要调用了 jpa 进行插入、删除、修改,就会开启两次数据库事务 哪怕我把 jpa 的这些操作放到 controller 里调用(controller 里没有 @Transaction 注解),只在调用 了 dao 的 save 方法( dao 接口继承了 JpaRepository 和 JpaSpe

js监听多个事件_《一文看懂浏览器事件循环》-爱代码爱编程

浏览器的事件循环标准是由 HTML 标准规定的,具体来说就是由whatwg规定的,具体内容可以参考event-loops in browser。而NodeJS中事件循环其实也略有不同,具体可以参考event-loops in nodejs 我们在讲解事件模型的时候,多次提到了事件循环。 事件指的是其所处理的对象就是事件本身,每一个浏览器都至少有一个事

jpa transaction 回滚_JPA的事务注解@Transactional使用总结-爱代码爱编程

在项目开发过程中,如果您的项目中使用了Spring的@Transactional注解,有时候会出现一些奇怪的问题,例如: 明明抛了异常却不回滚? 嵌套事务执行报错? ...等等 很多的问题都是没有全面了解@Transactional的正确使用而导致的,下面一段代码就可以让你完全明白@Transactional到底该怎么用。 直接上代码,请细细

分布式事务_spring-boot整合jpa-爱代码爱编程

前言 不啰嗦,直奔主题。。。 分布式事务解决方案目前主要有五种:1. XA 方案 2. TCC 方案 3. 本地消息表 4. 可靠消息最终一致性方案 5. 最大努力通知方案 今天主要记录下代码实测基于XA方案解决分布式事务的过程。 必要知识背景 事务四大特性:具备原子性、一致性、隔离性和持久性,简称 ACID。 分布式概念:分布式事务可以理解为在分布

女程序员晒出11月的工资条:工资是高,但是真累,说老十岁一点也不过分-爱代码爱编程

快过年了,网上晒工资的瞬间多了起来。有服务员、工人、护士、教师还有“程序猿”。程序员在大多数人眼里都是“高智商”,高薪资,那程序员到底一月的工资能开多少呢? 前两天有位女程序员在网上晒出了11月的工资条,说: 工资是高(比起有些人算是“高”吧),就是太累…… 她在帖文里还说: “出来工作几年了,工资确实是高,但现在,感觉工作的压力越来越大,现在都成

6 个月的开发,来面试居然要 18K , 我一问连 5K 都不值-爱代码爱编程

前言 2021 年 8 月份我的大学同学兼室友老左入职了深圳某家创业公司,刚入职还是很兴奋的,到公司一看老左说他傻了,公司除了他一个后端开发,公司的开发人员就只有 3 个前端 1 个测试还有 2 个 UI,在粗略了解公司的业务后才发现是一个从零开始的项目,目前啥都没有什么都需要自己搭建,老左就提出人手不够需要在招一个开发,由于他们公司就他一个 Java